<?php

    /*
        Поддерживаемые типы данных:
            1. Числовые типы: int, boolean (tinyint)
            2. Дата: datetime
            3. Строковые типы: file, picture, password, varchar
            4. Тестовые типы: text, html, php
            5. Объекты: object
            6. Массивы: object

        Добавить в описания полей:
            1. basetype
            2. length
            3. datagroup
            4. isarray
    */

    // элемент справочника
    class enum_item
    {
        public $name;
        public $value;
        public $description;
        public function __construct($name, $value, $description)
        {
            $this->name = $name;
            $this->value = $value;
            $this->description = $description;
        }
    }
    
    // справочник
    class sheme_enum
    {
        public $name;
        public $description;
        public $items;
        public function __construct($name, $description)
        {
            $this->name = $name;
            $this->description = $description;
            $this->items = Array();
        }
    }
    
    class js_validator_schemaclass
    {
        public $filters;
        public function __construct($filters = Array())
        {
            $this->filters = $filters;
        }
    }

    class js_filter_schemaclass
    {
        public $id;
        public $condition;
        public $invalidmethod;
        public function __construct($id = null, $condition = null, $invalidmethod = null)
        {
            $this->id = $id;
            $this->condition = $condition;
            $this->invalidmethod = $invalidmethod;
        }
    }

    // Поле сущности
    class entity_field
    {
        public $name;
        public $type;
        public $description;
        public $help;
        public $allownull = true;
        public $hidden;
        public $forsearch;
        public $unique;
        public $default;
        public $childattribute;
        public $validator;
        public $group;
        public function SetAttributeValue($name, $value)
        {
            switch($name)
            {
                case 'allownull':
                    $value = is_null($value)?true:(int)$value;
                    $value = $value != 0;
                    break;
                case 'forsearch': case 'hidden':
                    $value = is_null($value)?false:(int)$value;
                    $value = $value != 0;
                    break;
            }
            $this->$name = $value;
        }
        public function __construct($parameters = Array())
        {
            foreach($parameters as $name => $value)
            {
                $this->SetAttributeValue($name, $value);
            }
        }
    }

    // сущность
    class sheme_entity
    {
        public $name;
        public $description;
        public $template;
        public $inmenu;
        public $displayname;
        public $fields;
        public $childs;
        public function __construct($name = null, $description = null, $tostring = Array(), $inmenu = null, $searchresult = Array(), $url = Array())
        {
            $this->name = $name;
            $this->description = $description;
            $this->tostringformat = $tostring;
            $this->search_result_format = $searchresult;
            $this->inmenu = $inmenu;
            $this->url = $url;
            $this->fields = Array();
            $this->childs = Array();
        }
    }

    class mikron_entity_class
    {
        public $state = 0;
        public $order = 0;
        public $readed = 0;

        /*
        private $data = array();
        public function __construct()
        {
            $this->data = Array('state' => 0, 'order' => 0, 'readed' => false);
        }
        public function __set($attrname, $value)
        {
            $attrname = strtolower($attrname);
            if(array_key_exists($attrname, $this->data))
            {
                $this->data[$attrname] = $value;
            }
            else
            {
                throw new Exception("Отсутствует записываемый аттрибут `$attrname` у базового класса mikron_entity_class.");                
            }
        }
        public function __get($attrname)
        {
            $attrname = strtolower($attrname);
            if(array_key_exists($attrname, $this->data))
            {
                return $this->data[$attrname];
            }
            else
            {
                throw new Exception("Отсутствует записываемый аттрибут `$attrname` у базового класса mikron_entity_class.");                
            }
        }*/
    }
    
    class pre_entity
    {
        public $type = null, $cond = null, $entity = null, $id = null;
        function __construct($type, $cond, $entity, $id = null)
        {
            $this->type = $type;
            $this->cond = $cond;
            $this->entity = $entity;
            $this->id = $id;
        }
    }

    /**
    * cut: T_BASE_FUNCTIONS
    */

    // Базовый класс для всех сущностей
    class T_BASE
    {

        protected $id;
       // protected $CLASSNAME;
        public $mikron = Array('readed'=>null, 'order'=>null, 'state'=>null);
        // protected $isNew = true;

        public static $fields;
        public static $mikron_class_description;
        
        function description()
        {
            return self::$mikron_class_description;
        }

        /* not worked
        public function __construct()
        {
            $this->mikron = new mikron_entity_class();
        }*/
        // вынесенные функции
        public function DisplayName()
        {
            global $tbasefuncs;
            return $tbasefuncs->DisplayName($this);
        }
        public function toURL()
        {
            return $this->toString(false, true);
        }
        public function toString($for_search_result = false, $forurl = false)
        {
            global $tbasefuncs;
            return $tbasefuncs->toString($this, $for_search_result, $forurl);
        }
        // Вычитывание сущности из БД зная его ID
        protected function Load($id, ReturnAttributes $fields, $parent, $sqlrow=null)
        {
            global $tbasefuncs;
            return $tbasefuncs->Load($this, $id, $fields, $parent,$sqlrow);
        }
        protected function UpdateFields($id, $fields, $parent, $sqlrow = null)
        {
            global $tbasefuncs;
            if(! $fields instanceof ReturnAttributes)
            {
                $fields = new ReturnAttributes($fields);
            }
            
            // if(is_null($this->mikron)){$this->mikron = new mikron_entity_class();} // 13ms

            if(!is_null($sqlrow) && !is_null($id))
            {
                //$this->mikron['isnew'] = false;
                foreach($this->fields as $field)
                {
                    $fieldname = $field['name'];
                    $value = null;
                    $readed = true;                    
                    if($this instanceof T_OPERATIONS_LOG)
                    {

                    }
                    else
                    {
                        $this->$fieldname = new getset();
                    }
                }
                return $this->Load($id, $fields, $parent, $sqlrow);
               
            }
            
            /*if($this instanceof T_OPERATIONS_LOG)
            {
                return;
            }*/
            
            // Если сущность новая, тогда просто заполняем аттрибуты значениями по умолчанию
            if(!$tbasefuncs->UpdateFields($this, $id, $fields, $parent)) // 75ms
            {
                // Если сущность загруженная, тогда загружаем её
                //$this->mikron['isnew'] = false;
                $this->Load($id, $fields, $parent);               
            }
        }
        function SetBaseAttributes($vid, $vmikron_state, $vmikron_order)
        {
            $this->id = (int)$vid;
            $this->mikron['state'] = (int)$vmikron_state;
            $this->mikron['order'] = (int)$vmikron_order;
        }
        public function __set($var, $value)
        {
            CriticalError("Отсутствует аттрибут `$var` у сущности <strong>".get_class($this)."</strong>, либо он доступен только для чтения.");
        }
        
        public function get2($var)
        {
            return $this->$var;
        }

        public function __get($var)
        {
            // $var = strtolower($var);
            switch($var)
            {
                case "id":
                    return $this->id;
                    break;
                case "fields":
                    // return $this->filelds(); //$this->fields;
                    break;
                case "description":
                    return self::$mikron_class_description;
                    break;
                case "isnew":
                    return is_null($this->id); //->mikron['isnew'];
                    break;
                case "mikron":
                    return $this->mikron;
                    break;
                default:
                    $classname = get_class($this);
                    throw new Exception("Отсутствует аттрибут `$var` у сущности $classname.");
                    return null;
                    break;
            }
        }

        public function Validate()
        {
            return self::ValidateFunction($this);
        }

        private static function ValidateFunction($object)
        {
            global $mikron;
            foreach($object->fields as $field)
            {
                $field_name = $field['name'];
                $field_type = $field['type'];
                $field_editmode = $field['editmode'];
                $field_dbtype = $field['dbtype'];
                $field_dbtype_base = strtolower(getbasetype($field_dbtype));
                $field_type_base = strtolower(getbasetype($field_type));
                $field_value = null;
                if(!in_array($field_editmode, Array("parent", "childattribute")) && !is_null($field_value = $object->$field_name->value))
                {
                    if(($object->$field_name->modified)|| ($field_type == "datetime"))
                    {
                        switch(strtolower($field_dbtype_base))
                        {
                            case "int":
                                if(!is_numeric($field_value) && !$data_is_null)
                                {
                                    return $mikron->Exceptions->InvalidDataException($field);
                                }
                                $object->$field_name->value = (int)$field_value;                                
                                break;
                            case "datetime";
                                if(($field_value = date($mikron->Constants->DateTimeFormatMySQL, strtotime($field_value))) == -1)
                                {
                                    return $mikron->Exceptions->InvalidDataException($field);
                                }
                                $object->$field_name->value = date($mikron->Constants->DateTimeFormatMySQL, strtotime($field_value));
                                break;
                        }
                        switch($field_type_base)
                        {
                            case "boolean":
                                if(!is_numeric($field_value) && !is_bool($field_value))
                                {
                                    return $mikron->Exceptions->InvalidDataException($field);
                                }
                                $object->$field_name->value = $field_value?1:0;
                                break;
                            case 'password':
                                // $object->$field_name->value = md5($field_value);
                                break;
                        }
                    }
                }
            }
            return true;
        }

        /**
        * Сохранение сущности
        * 
        * @param mixed $always If there is nothing to save then save display_name only.
        */
        public function Save($always = false)
        {

            // Валидация данных
            if(($exception = $this->Validate()) !== true)
            {
                // Генерируется исключение при ошибках валидации
                throw new MikronException($exception->getMessage());
            }

            $isnew = $this->isNew;
            $entityname = get_class($this);
            $fields = Array();
            $values = Array();
            $fieldsandvalues = Array();

            // Define of the list of saved attributes
            foreach($this->fields as $field)
            {
                $fieldname = $field['name'];
                if($this->$fieldname->readed|| $this->$fieldname->modified)
                {
                    $childattribute = $field['childattribute'];
                    if(!is_null($childattribute))
                    {
                        // Save childlists
                        if(is_array($field_array = $this->$fieldname->value))
                        {
                            foreach($field_array as $child)
                            {
                                $child->Save();
                            }
                        }
                    }
                    // $fieldtype = $field['type'];
                    // $fieldtype_base = strtolower(getbasetype($fieldtype));
                    if($this->$fieldname->modified) //|| ($fieldtype == "datetime"))
                    {
                        $field_dbtype = $field['dbtype'];
                        $field_type = $field['type'];
                        if($field_type == 'password')
                        {
                            if(!$this->$fieldname->isnull)
                            {
                                $this->$fieldname->value = md5($this->$fieldname->value);
                            }
                        }
                        $field_dbtype_base = getbasetype($field_dbtype);
                        $fieldvalue = $this->$fieldname->value;
                        $fieldname2 = $fieldname;
                        $fieldvalue2 = is_null($fieldvalue)?"NULL":quote_smart($fieldvalue);
                        $fields[] = $fieldname2;
                        $values[] = $fieldvalue2;
                        $fieldsandvalues[] = "`$fieldname2` = $fieldvalue2";
                    }
                }
            }

            if((count($fields) < 1) && !$always)
            {
                // If nothing to save
                return null;
            }

            $mikron_order = 1;
            $displayname = quote_smart($this->DisplayName());

            if($isnew)
            {
                $sql = "SELECT MAX(`mikron_order`) AS maxordervalue FROM $entityname";
                $result = @mysql_query($sql) or Exception2("Ошибка сохранения сущности $entityname.<br /><code>$sql</code><br />".mysql_error());
                if(!mysql_errno())
                {
                    $mikron_order = (int)mysql_result($result, 0, "maxordervalue");
                    mysql_free_result($result);
                    $mikron_order++;
                }
                $fields[] = 'mikron_order';
                $values[] = (int)$mikron_order;
                $fields[] = 'mikron_displayname';
                $values[] = $displayname;
                $fields_sql = Array();
                foreach($fields as $fname)
                {
                    $fields_sql[] = "`$fname`";
                }
                $fields2 = implode(", ", $fields_sql);
                $values2 = implode(", ", $values);
                $sql = "INSERT INTO $entityname($fields2) VALUES($values2)";
            }
            else        
            {
                $fieldsandvalues[] = "`mikron_displayname` = $displayname";
                $fieldsandvalues2 = implode(", ", $fieldsandvalues);
                $id = (int)$id;
                $sql = "UPDATE $entityname SET $fieldsandvalues2 WHERE `id` = $id";
            }
            @mysql_query($sql) or Exception2("Ошибка сохранения сущности $entityname.<br /><code>$sql</code><br />".mysql_error());
            if($isnew)
            {
                $this->mikron['order'] = $mikron_order;
            }

            /**
            * Сброс флагов IsModified для всех
            * атрибутов сохраненного объекта.
            */
            foreach($this->fields as $field)
            {
                $fieldname = $field['name'];
                if($this->$fieldname->modified)
                {
                    $this->$fieldname->modified = false;
                }
            }

            if($isnew)
            {
                $id = mysql_insert_id();
                $this->id = $id;
            }

            // don't put in log
            return;

            if($entityname != get_class($rec))
            {
                // Логирование действий с сущностями
                /*
                $rec = new T_LOGRECORD();
                if($isnew)
                {
                    $rec->message->value = "Добавлен новый экземпляр сущности $entityname, id = $id";
                }
                else
                {
                    if(count($fields) == 1)
                    {
                        $rec->message->value = "У сущности $entityname, id = $id изменено поле $fields2.";
                    }
                    else
                    {
                        $rec->message->value = "У сущности $entityname, id = $id изменены поля $fields2.";
                    }
                }
                global $mikron;
                $enum = $mikron->Enumerators->getEnum('LOG_CATEGORY');
                $category = $enum['info'];
                $rec->category->value = $category['value'];
                $rec->dt->value = date($mikron->Constants->DateTimeFormat);
                $rec->Save();
                */
            }
        }
    }

    /**
    * cut: Вырезан класс getset
    */

    function ConvertToListItem($item)
    {
        if(array_key_exists('title', $item))
        {
            return $item->title->value;
        }
        else
        {
            if(is_object($item))
            {
                return $item->DisplayName();
            }
            else
            {
                return null;
            }
        }
    }

    function getbasetype($type)
    {
        $pos = strpos($type, '(');
        if($pos === false){return $type;}
        else
        {
            return substr($type, 0, $pos);
        }
    }

    function getbaseLength($type)
    {
        $pos = strpos($type, '(');
        if($pos === false){return 0;}
        else
        {
            $ln = substr($type, $pos + 1);
            $pos = strpos($ln, ')');
            if($pos === false){return 0;}
            $ln = substr($ln, 0, $pos);
            return (int)$ln;
        }
    }

    // Получение сл. индекса автоинкрементного поля для определенной таблицы
    function get_table_auto_increment_next_value($table) 
    { 
      $sql = 'SHOW TABLE STATUS FROM `'.constant('bdname')."` LIKE '$table'"; 
      $result = mysql_query($sql); 
      $row = mysql_fetch_assoc($result);
      return $row['Auto_increment']; 
    }

    class enums_class
    {
        public $enums;
        function __construct()
        {
            $result = mysql_query('SELECT * FROM ENUMS WHERE parent = 0');
            if(mysql_errno())
            {
                return new MikronException('Ошибка чтения справочников. '.mysql_error());
            }
            while($row = mysql_fetch_object($result))
            {
                $enumname = $row->name;
                $newenum = Array();
                $enumid = $row->id;
                $result2 = mysql_query("SELECT * FROM ENUMS WHERE parent = '$enumid'");
                while($row2 = mysql_fetch_object($result2))
                {
                    $enum = Array();
                    $name = $row2->name;
                    $description = $row2->description;
                    $value = $row2->value;
                    $enum['name'] = $name;
                    $enum['description'] = $description;
                    $enum['value'] = $value;
                    $newenum[$name] = $enum;
                }   
                $this->enums[$enumname] = $newenum;  
            }
        }

        function add($name, $description, $value = 0, $parent =  0)
        {
            $name2 = quote_smart($name);
            $description2 = quote_smart($description);
            $value = (int)$value;
            $parent = (int)$parent;
            $sql = "INSERT INTO ENUMS(`name`, `description`, `value`, `parent`) VALUES($name2, $description2, $value, $parent)";
            $result = mysql_query($sql);
            return mysql_insert_id();
        }
        
        function getEnumNameById($enum_name, $id)
        {
            $enums = $this->getEnum($enum_name);
            foreach($enums as $e)
            {
                if($e['value'] == $id)
                {
                    return $e['description'];
                }
            }
            return null;
        }

        function getEnum($enumname)
        {
            if(isset($this->enums[$enumname]))
            {
                return $this->enums[$enumname];
            }
            else
            {
                return null;
            }
        }

        public function clear()
        {
            $sql = "TRUNCATE TABLE ENUMS";
            $result = mysql_query($sql);
        }

    }
    
    class nullclass
    {
        
    }

    class entitymanager_class
    {

        private $entityfile;
        private $entityfile_update;
        public $Types;
        public $SavedItem;
        private $transaction_started = false;
        private $mikron = null;
        
        private $save_return_page = null;
        private $save_error_page = null;
        private $save_entity_name = null;
    
        function Items()
        {
            $a_entities = Array();
            $result = mysql_query("SELECT * FROM ENTITIES");
            if(!mysql_errno())
            {
                while($row = mysql_fetch_object($result))
                {
                    $a_entities[] = $row->name;
                }
            }
            return $a_entities;
        }
        
        public function __construct($mikron)
        {
            $this->mikron = $mikron;
            $help_message_box = "<div class=\"help_message_box\">%help%</div>";
            $types = Array();
            $this->entityfile = constant('ENTITIES_CLASSES_FILE');
            $this->entityfile_update = str_replace('.inc', '', constant('ENTITIES_CLASSES_FILE')).".tmp";
            $this->SetEditorControl('file', "varchar(250)", "file", "<a href=\"%value%\">Посмотреть файл</a><br /><input size=\"48\" type=\"file\" name=\"%name%\" id=\"%labelname%\"><br />$help_message_box");
            $this->SetEditorControl('picture', "varchar(250)", "file", "<input size=\"48\" type=\"file\" name=\"%name%\" id=\"%labelname%\" /><br />$help_message_box");
            $this->SetEditorControl('int', "int", "string", "<input type=\"text\" size=\"%inputsize%\" name=\"%name%\" value=\"%value%\" id=\"%labelname%\" /><br />$help_message_box");
            $this->SetEditorControl('boolean', "tinyint", "boolean", array('Да'=>1, 'Нет'=>0));
            $this->SetEditorControl('datetime', "datetime", "string", "<input type=\"text\" size=\"%inputsize%\" name=\"%name%\" value=\"%value%\" id=\"%labelname%\" /><br />$help_message_box");
            $this->SetEditorControl('password', "varchar(%length%)", "password", "<input size=\"%inputsize%\" name=\"%name%\" id=\"%labelname%\" type=\"password\" />%repass%<br />$help_message_box", 1, '<br />Повторите пароль:
                                                                                                                                                                                                  <br /><input size="48" name="repass[%name%]" id="repass_%labelname%" type="password" />');
            $this->SetEditorControl('varchar', "varchar(%length%)", "string", "<input type=\"text\" size=\"%inputsize%\" maxlength=\"%length%\" name=\"%name%\" value=\"%value%\" id=\"%labelname%\" /><br />$help_message_box");
            $this->SetEditorControl('text', "longtext", "text", "<textarea style=\"resize: both; overflow: auto;\" id=\"%labelname%\" cols=\"%textarea_cols%\" rows=\"%textarea_rows%\" name=\"%name%\">%htmlvalue%</textarea><br />%help%", 2);
            $this->SetEditorControl('html', "longtext", "text", "<textarea style=\"resize: both; overflow: auto;\" id=\"htmlarea\" cols=\"%textarea_cols%\" rows=\"%textarea_rows%\" name=\"%name%\">%htmlvalue%</textarea><br />$help_message_box", 2);
            $this->SetEditorControl('php', "longtext", "text", "<textarea style=\"resize: both; overflow: auto;\" id=\"%labelname%\" class=\"codearea\" cols=\"%textarea_cols%\" rows=\"%textarea_rows%\" name=\"%name%\">%htmlvalue%</textarea><br />$help_message_box", 2);
        }

        private function SetEditorControl($type, $dbtype, $editMode, $editor, $columns = 1, $extend_form = null)
        {
            // $type - тип указанный в XML
            // $dbtype - подготовленный тип дл БД
            // $editMode - тип контрола для редактирования
            // $editor - контрол для редактирования
            $this->Types[$type] = Array('dbtype' => $dbtype, 'editmode' => $editMode, 'editor' => $editor, 'columns' => (int)$columns, 'extend_form' => $extend_form);
        }

        public function can_clear()
        {
            
        }

        public function clear()
        {
            $result = mysql_query("TRUNCATE TABLE ENTITIES");
            if(file_exists($this->entityfile_update))
            {
                chmod($this->entityfile_update, 0777);
                unlink($this->entityfile_update) or Exception2("Ошибка удаления файла с сущностями.", 1023);
            }
        }

        function getvalue($value)
        {
            if($value->readed|| $value->modified)
            {
                return $value->value;
            }
            elseif(!is_null($value->value))
            {
                return $value->value;
            }
        }

        public function ShowEntityAsList($parameters)
        {
            global $mikron;
            $entityname = $parameters['entityname'];
            $items = $mikron->Queries->Query($entityname);
            echo "<ul>";
                while($item = $items->fetch())
                {
                    echo "<li>", $item->toString(false, true), "</li>";
                }
            echo "</ul>";
        }

        public function LoadEntity($parameters)
        {
            global $mikron;
            $entityname = $parameters['entityname'];
            $name = $parameters['as'];
            $e = $mikron->EntityManager->Query($entityname);
            $mikron->TemplateMaker->smarty->register_object($name, $e);
            $mikron->TemplateMaker->smarty->assign_by_ref($name, $e);
        }

        public function ShowEntityAsTable($parameters)
        {
            $entityname = $parameters['entity'];
            $columns = (int)MikronToolsClass::GetParam($parameters, 'columns', 0);
            $maxitems = (int)MikronToolsClass::GetParam($parameters, 'maxitems', 0);
            $callspacing = (int)MikronToolsClass::GetParam($parameters, 'callspacing', 0);
            $cellpadding = (int)MikronToolsClass::GetParam($parameters, 'cellpadding', 0);
            $width = MikronToolsClass::GetParam($parameters, 'width', null);
            $class = MikronToolsClass::GetParam($parameters, 'class', null);
            $where = MikronToolsClass::GetParam($parameters, 'where', null);
            $htable1_items = $this->mikron->Queries->Query($entityname, null, $where);
            $htable1_count = 0;
            $htable1 = new html_table($columns, $class, $width, $callspacing, $cellpadding);
            while($htable1_item = $htable1_items->fetch())
            {
                $htable1->newcol($htable1_item->toString());
                $htable1_count++;
                if(($maxitems > 0) && ($htable1_count >= $maxitems))
                {
                    break;
                }
            }
            $htable1->end();
        }

        public function RestoreEntity($entityname, $id)
        {
            $a = Array();
            if(is_array($id))
            {
                foreach($id as $value)
                {
                    $a[] = (int)$value;
                }
            }
            else
            {
                $a[] = (int)$id;
            }
            $id = implode(", ", $a);
            @mysql_query("UPDATE $entityname SET mikron_state = 0 WHERE id IN($id)") or CriticalError("Ошибка восстановления объекта $entityname, где id = $id.<br />".mysql_error());
        }
    
        public function DeleteEntity($entityname, $id, $phisicaly = false)
        {
            if($entityname === false)
            {
                return;
            }
            $a = Array();
            if(is_array($id))
            {
                foreach($id as $value)
                {
                    $a[] = (int)$value;
                }
            }
            elseif($id=='*')
            {
                $a[] = '*';
            }
            else
            {
                $a[] = (int)$id;
            }
            $id = implode(", ", $a);
            if($phisicaly)
            {
                // вычитка полей файловых типов
                $e = new $entityname();
                $filefields = Array();
                $filefields_names = Array();
                foreach($e->fields as $field)
                {
                    switch($field['type'])
                    {
                        case "file": case "picture":
                            $filefields[] = "`".$field['name']."`";
                            $filefields_names[] = $field['name'];
                            break;
                    }
                }
                // если есть поля файловых типов
                if(count($filefields) > 0)
                {
                    $filefields2 = implode(", ", $filefields);
                    if($id == '*')
                    {
                        $request = @mysql_query("SELECT $filefields2 FROM $entityname") or CriticalError("Ошибка при удалении объекта $entityname.<br />".mysql_error());                        
                    }
                    else
                    {
                        $request = @mysql_query("SELECT $filefields2 FROM $entityname WHERE `id` IN($id)") or CriticalError("Ошибка при удалении объекта $entityname, где id = $id.<br />".mysql_error());
                    }
                    while($row = mysql_fetch_object($request))
                    {
                        foreach($filefields_names as $fname)
                        {
                            // проверка существования
                            $file = $_SERVER['DOCUMENT_ROOT'].$row->$fname;
                            if(file_exists($file))
                            {
                                // удаление файла
                                unlink($file);
                            }
                        }
                    }
                }
                $whereCause = null;
                if($id == '*')
                {
                    @mysql_query("TRUNCATE TABLE $entityname") or CriticalError("Ошибка очистки таблицы $entityname.<br />".mysql_error());
                }
                else
                {
                    $whereCause = "WHERE `id` IN($id)";
                    @mysql_query("DELETE FROM $entityname $whereCause") or CriticalError("Ошибка удаления $entityname, где id = $id.<br />".mysql_error());
                }
            }
            else
            {
                $whereCause = null;
                if($id != '*')
                {
                    $whereCause = "WHERE `id` IN($id)";
                }
                @mysql_query("UPDATE $entityname SET `mikron_state` = 1 $whereCause") or CriticalError("Ошибка удаления объекта $entityname, где id = $id.<br />".mysql_error());
            }
        }

        private function CheckDelete()
        {
            global $mikron;
            if(!isset($_REQUEST['entityname']))
            {
                return;
            }
            $entityname = $_REQUEST['entityname'];
            $entityname = $mikron->Crypt->Decrypt($entityname);
            // удаление
            if(isset($_REQUEST['delete']) && isset($_REQUEST['id']) )
            {
                $id = (int)$_POST['id'];
                $this->DeleteEntity($entityname, $id);
                ob_end_clean();
                header('Location: '.$_SERVER['SCRIPT_NAME']."?ok=delete&entityname=$entityname");
                exit;
            }
            // групповое удаление
            if(isset($_POST['groupoperation']) && is_array($_POST['groupoperation']))
            {
                $operation = $_REQUEST['groupoperation'];
                if(array_key_exists("deleteselectitems", $operation))
                {
                    if(is_array($_POST['selecteditems']))
                    {
                        $items = $_POST['selecteditems'];
                        $this->DeleteEntity($entityname, $items, true);
                        ob_end_clean();
                        header('Location: '.$_SERVER['SCRIPT_NAME']."?ok=delete&entityname=$entityname");
                        exit;
                    }
                }
            }
        }

        private function CheckRestore()
        {
            global $mikron;
            if(!isset($_REQUEST['entityname']))
            {
                return;
            }
            $entityname = $_REQUEST['entityname'];
            $entityname = $mikron->Crypt->Decrypt($entityname);
            // восстановление
            if(isset($_REQUEST['restore']) && isset($_REQUEST['id']))
            {
                 $id = (int)$_REQUEST['id'];
                $this->RestoreEntity($entityname, $id);
                ob_end_clean();
                header('Location: '.$_SERVER['SCRIPT_NAME']."?ok=restore&entityname=$entityname");
                exit;
            }
        }

        private function CheckChangeOrder()
        {
            global $mikron;
            if(!isset($_REQUEST['entityname']))
            {
                return;
            }
            $entityname = $_REQUEST['entityname'];
            if(strpos('#', $entityname) !== false)
            {
                $entityname = $mikron->Crypt->Decrypt($entityname);
            }
            // изменение порядка
            if(isset($_GET['changeorder']) && isset($_GET['id']))
            {
                $id = (int)$_GET['id'];
                $side = $_GET['changeorder'];
                $e = new $entityname($id);
                $order = $e->mikron['order']; // текущая позиция
                switch($side)
                {
                    case 'up':
                        $r = mysql_query("SELECT * FROM $entityname WHERE `mikron_order` < $order LIMIT 0 ,1");
                        break;
                    case 'down':
                        $r = mysql_query("SELECT * FROM $entityname WHERE `mikron_order` > $order ORDER by `mikron_order` LIMIT 0 ,1");
                        break;
                    default:
                        return;
                        break;
                }
                if(mysql_num_rows($r) < 1)
                {
                    return;
                }
                $neworder = mysql_result($r, 0, 'mikron_order');
                $swapid = mysql_result($r, 0, 'id');
                mysql_query("UPDATE $entityname SET `mikron_order` = $neworder WHERE `id` = $id");
                mysql_query("UPDATE $entityname SET `mikron_order` = $order WHERE `id` = $swapid");
                if(isset($_GET['supclass']))
                {
                    $entityname = $_GET['supclass'];
                    $sup_id = (int)$_GET['supid'];
                    $uri = $_SERVER['SCRIPT_NAME']."?edit=&amp;entityname=$entityname&amp;id=".$sup_id;
                }
                else
                {
                    $uri = $_SERVER['SCRIPT_NAME']."?ok=changeorder&entityname=$entityname";
                }
                $this->mikron->Tools->GoToPage($uri);
            }
        }

        function CheckSave($userform = null)
        {

            $this->CheckDelete();
            $this->CheckChangeOrder();
            $this->CheckRestore();

            $handlers = $this->mikron->Handlers->Handlers(HANDLER_SAVE);
            $this->save_entity_name = null;
            
            $result = null;

            /**
            * Сохранение сущности
            */
            try
            {

                /**
                * Обработка данных с заполненной формы
                */
                $result = $this->CheckSave_Function($userform);

                if(!is_object($result))
                {
                    return null;
                }

                foreach($handlers as $handler)
                {
                    if(in_array($this->save_entity_name, $handler->entities))
                    {
                        $processor = $handler->processor;
                        $object = is_object($handler->object)?$handler->object:$this->mikron->Site;
                        if(!$object->$processor($result)){return false;}
                    }
                }

                // Сохраняем сущность
                try
                {
                    $result->Save();
                    unset($_SESSION['formdata'], $_SESSION['error']); 
                }
                catch(Exception $ex)
                {
                    if(is_null($userform))
                    {
                        CriticalError($ex->getMessage(), null, true);
                    }
                    else
                    {
                        throw new Exception($ex->getMessage(), $ex->getCode());
                    }
                }

                try
                {
                    $this->SavedItem = $result;
                    $_SESSION['SavedItem'] = $result;
                    if(!is_null($userform))
                    {
                        return $result;
                    }
                    $returntopage = $this->save_return_page;
                    if(is_null($returntopage))
                    {
                        if(isset($_REQUEST['flag_privateform']))
                        {
                            $returntopage = $_SERVER['REQUEST_URI'];
                            $pos = strpos($returntopage, "?");
                            if($pos !== false)
                            {
                                $returntopage = substr($returntopage, 0, $pos);
                            }
                            $returntopage = $returntopage."?ok=add&entityname=".$this->save_entity_name;
                        }
                        else
                        {
                            $returntopage = $_SERVER['SCRIPT_NAME'].'?ok';
                        }
                    }
                    $returntopage = str_replace('%ID%', $result->id, $returntopage);
                    ob_end_clean();
                    header('Location: '.$returntopage);
                    exit;
                }
                catch(Exception $ex)
                {
                    throw new Exception('Ошибка при сохранении. '.$ex->getMessage(), $ex->getCode());
                }

            }
            catch(Exception $ex)
            {
                $_SESSION['ErrorItem'] = $result;
                if(!is_null($userform))
                {
                    throw new Exception($ex->getMessage(), $ex->getCode());
                }
                foreach($handlers as $handler)
                {
                    if(in_array($this->save_entity_name, $handler->entities))
                    {
                        $processor = $handler->processor;
                        $object = is_object($handler->object)?$handler->object:$this->mikron->Site;
                        if(!$object->$processor(new MikronException($ex->getMessage(), $ex->getCode(), $ex))){return false;}
                    }
                }
                global $mikron;
                $mikron->Tools->GoToPage($this->save_error_page);
            }

        }

        function CheckSave_Function($userform = null)
        {
            
            $data_from_public_form = false;

            // сохранение 
            if(!isset($_POST['saveentityform']))
            {
                return null;
            }

            // список полей сохраняемой сущности
            if(isset($_POST['entity']))
            {
                $entity = $_POST['entity'];
            }
            elseif(isset($_POST['userform']))
            {
                $entity = $_POST['userform'];
                $data_from_public_form = true;
            }
            elseif(!is_null($userform))
            {
                if(isset($_POST[$userform]))
                {
                    $entity = $_POST[$userform];
                    if(!is_array($entity))
                    {
                        return null;
                    }
                    $data_from_public_form = true;
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }

            $_SESSION['formdata'] = $entity;

            // Адрес, куда перебросит при ошибке
            $errorpage = null;
            
            // Адрес куда перебросит при успешном сохранении
            $returntopage = null;

            if(isset($entity['_errorpage']) && !is_null($entity['_errorpage']) && $entity['_errorpage'] != '')
            {
                $errorpage = $entity['_errorpage'];
            }
            if(array_key_exists('_returntopage', $entity))
            {
                if(!is_null($entity['_returntopage']) && $entity['_returntopage'] != '')
                {
                    $returntopage = $entity['_returntopage'];
                    unset($entity['_returntopage']);
                }
            }
            $this->save_error_page = $errorpage;
            $this->save_return_page = $returntopage;

            $formdata = $entity;
            // Если не указан класс, то дальше делать нечего (некорректная форма)
            if(!isset($formdata['classname'])|| !isset($formdata['classname2']))
            {
                return null;
            }

            $classname = $formdata['classname'];
            $entityname = $classname;
            $this->save_entity_name = $entityname;   

            if(isset($_REQUEST['captcha']) && isset($_SESSION))
            {
                if($_SESSION['captcha'] != $_REQUEST['captcha'])
                {
                    $_SESSION['formdata'] = $formdata;
                    $_SESSION['captcha'] = null;
                    $_SESSION['error'] = 'Указан неверный код подтверждения.';
                    throw new Exception('Указан неверный код подтверждения.', 4567);
                }
                $_SESSION['captcha'] = null;
            }

            /**
            * Проверка наличия базовых полей
            */
            if(!isset($formdata['encodekey']))
            {
                throw new Exception('У сохраняемой сущности отсутствует служебный аттрибут.');
            }
            $id = isset($formdata['id'])?(int)$formdata['id']:null;

            $classname_enc = $formdata['classname2'];
            $encodekey = $formdata['encodekey'];
            if(md5("ecn_$encodekey".$classname) != $classname_enc)
            {
                throw new Exception('Ошибка безопасности.');
            }

            // Создаем, либо загружаем экземпляр сущности
            $e = new $entityname($id);
            
            // Список полей, которые нужно установить в NULL. Только поля файловых типов.
            $settonullfields = Array();
            if(isset($_POST['settonull']))
            {
                foreach($_REQUEST['settonull'] as $key => $value)
                {
                    $settonullfields[$key] = 'settonull';
                }
            }

            // Проверяем наличие полей типа «Файл»
            if(isset($_FILES['entity']))
            {
                $entityfiles = $_FILES['entity'];
                $uploaddir = constant('CURRENTSITE_DIR').'/uploads/';
                foreach($entityfiles['name'] as $key => $name)
                {
                    if($entityfiles['error'][$key] == '0')
                    {
                        if(!array_key_exists($key, $e->fields))
                        {
                            throw new Exception("Неверное имя поля загружаемого файла `$key`.");
                        }
                        $f = $e->fields[$key];
                        $basetype = getbasetype($f['type']);
                        $abt = Array("file", "picture");
                        if(!in_array($basetype, $abt))
                        {
                            throw new Exception("Запрещено загружать файлы в свойство с типом `$basetype`.");
                        }
                        $temp = $entityfiles['tmp_name'][$key];         
                        $userfilename = $entityfiles['name'][$key]; 
                        if (is_uploaded_file($temp))
                        {
                            // MIME-тип файл
                            $type = $entityfiles['type'][$key];
                            // размер загруженного файла в байтах
                            $size = $entityfiles['size'][$key];
                            if(file_exists($uploaddir.$userfilename))
                            {
                                $uploadfile = $uploaddir.str_replace('.', '', strtolower(basename($temp))).'_'.$userfilename;
                            }
                            else
                            {
                                $uploadfile = $uploaddir.strtolower(strtolower($mikron->Tools->String->Transliterate($userfilename)));
                            }
                            if (move_uploaded_file($temp, $uploadfile))
                            {
                                $documentroot = $_SERVER['DOCUMENT_ROOT'];
                                $uploadfile = correctPath(realpath($uploadfile));
                                $uploadfile = str_replace($documentroot, "", $uploadfile);
                                if(file_exists($documentroot.$e->$key->value))
                                {
                                    unlink($documentroot.$e->$key->value);
                                }
                                $e->$key->value = $uploadfile;
                            }
                            else
                            {
                                throw new Exception("Не удалось переместить загруженный файл в $uploadfile. Возможно не проставлены разрешения для папки.");
                            }
                        }
                        else
                        {
                            $error = $entityfiles['error'][$key];
                            if($error != 4)
                            {
                                throw new Exception("Ошибка №$error при загрузке файла.");
                            }
                        }
                    }
                }
            }

            global $mikron;

            // Заполнение полей сущности данными из полей формы
            foreach($formdata as $key => $value)
            {
                if(array_key_exists($key, $e->fields))
                {
                    if($value == '')
                    {
                        $value = null;
                    }
                    $f = $e->fields[$key];
                    $need_set_value = true;
                    if($data_from_public_form)
                    {
                        if($f['hidden'])
                        {
                            $value = $mikron->Crypt->Decrypt($value, $encodekey);
                        }
                    }
                    if($need_set_value)
                    {
                        $basetype = getbasetype($f['type']);
                        switch($basetype)
                        {
                            case 'boolean':
                                if(!is_numeric($value) && !is_bool($value))
                                {
                                    throw new Exception("Ошибка при сохранении
                                    значения поля $key. Неверный тип значения.");
                                }
                                else
                                {
                                    $e->$key->value = $value?1:0;
                                }
                                break;
                            case 'picture': case 'file':
                                break;
                            case 'password':
                                if(!is_null($value))
                                {
                                    $e->$key->value = $value;
                                }
                                break;
                            default:
                                if(is_null($id) && is_null($value))
                                {
                                    
                                }
                                else
                                {
                                    $e->$key->value = $value;
                                }
                                break;
                        }
                    }
                }
            }

            // extra operations with fields
            foreach($e->fields as $field)
            {
                $fieldname = $field['name'];
                if(array_key_exists($fieldname, $settonullfields))
                {
                    $operation = $settonullfields[$fieldname];
                    switch($operation)
                    {
                        case 'settonull':
                        {
                            $e->$fieldname->value = null;
                            break;
                        }
                    }
                }
            }
            
            if(is_null($id))
            {
                // MikronBaseObject::ResetDefaultFields($e);
            }

            return $e;

        }

        // Корректность имени сущности
        public function CheckEntityName($entityname)
        {
            return class_exists($entityname);
        }

        // Считывает коллекцию удаленных сущностей определенного типа
        public function QueryDeleted($entityname)
        {
            if($this->CheckEntityName($entityname))
            {
                $sql = "SELECT id FROM $entityname WHERE `mikron_state` = 1 ORDER by `mikron_order` DESC";
                $ret = Array();
                $result = mysql_query($sql);
                while($row = mysql_fetch_object($result))
                {
                    $id = $row->id;
                    $ret[] =  new $entityname($id);
                }
                return $ret;
            }
        }

        /**
        *  Возвращает количество сущностей определенного типа по заданным условиям
        * @param mixed $entityname
        * @param mixed $aId Id of entity
        * @param mixed $conditions Criterion or Criteria
        */
        public function QueryCount($entityname, $aId = null, $conditions = null)
        {
            return $this->Query($entityname, $aId = null, $conditions, null, null, null, true);
        }

        function QueryDisplayNames($entityname, $aId = null, $conditions = null, $order = null, $limit = null)
        {

            $ret = Array();
            $fields = Array('mikron_displayname');

            /*
            global $mikron;
            $e = $mikron->Queries->Query($entityname, $aId, $conditions, $order, $limit, $fields);
            while($object = $e->fetch())
            {
                $ret[] = Array('id'=>$object->id, 'displayname'=>$object->mikron_displayname->value);
            }
            return $ret;
            */

            $sql = $this->CompileQueryParameters($entityname, $aId, $conditions, $order, $limit, $fields);
            $r = mysql_query($sql);
            while($row = mysql_fetch_object($r))
            {
                $ret[] = Array('id'=>$row->id, 'displayname'=>$row->mikron_displayname);
            }
            mysql_free_result($r);
            return $ret;

            /*
            $entityname2 = quote_smart($entityname);
            $r = mysql_query("select `displayname` from entities where `name` = $entityname2");
            $dn = mysql_result($r, 0, 'displayname');
            $out = null;
            $pattern = '|\{\{[a-zA-Z_]{2,12}\}\}|i';
            preg_match_all($pattern, $dn, $out);
            if(count($out) < 1)
            {
                return $this->Query($entityname, $aId, $conditions, $order, $limit);
            }
            $out = $out[0];
            $fields = Array();
            foreach($out as $o)
            {
                $o = str_replace('{{', null, $o);
                $o = str_replace('}}', null, $o);
                $fields[] = $o;
            }
            return $this->Query($entityname, $aId, $conditions, $order, $limit, null, false, $fields, true);
            */
        }
        
        /**
        * put your comment there...
        * 
        * @param mixed $entityname Name of table for entity
        * @param string $aId Number, array or symbol '*'
        * @param mixed $conditions
        * @param mixed $order Format: `title` ASC
        * @param mixed $limit Format: M,N
        * @param ReturnAttributes $readed_fields Вычитываемые из БД аттрибуты
        */
        public function CompileQueryParameters($entityname, $aId = null, $conditions = null, $order = null, $limit = null, $readed_fields = null, $calculate_count_only = false)
        {

            if(!$this->CheckEntityName($entityname))
            {
                throw new Exception('Unknown entity name '.var_export($entityname, true));
            }
            if(!$readed_fields instanceof ReturnAttributes)
            {
                $readed_fields = new ReturnAttributes($readed_fields);
            }
            $fast_fields = clone $readed_fields;
            if($fast_fields->Count() > 0)
            {
                $fast_fields->add('id');
                $fast_fields->add('mikron_state');
                $fast_fields->add('mikron_order');
                $fast_fields->add('mikron_displayname');
            }
            $readedfields = $fast_fields->ImplodeSQL();

            // init ID
            if(!is_null($aId))
            {
                if(is_array($aId))
                {
                    $v = Array();
                    foreach($aId as $id)
                    {
                        $v[] = (int)$id;
                    }
                    $aId = implode(", ", $v);
                }
                elseif(is_numeric($aId))
                {
                    $aId = (int)$aId;
                }
                elseif($aId == '*')
                {
                    
                }
                else
                {
                    throw new Exception('Invalid ID for entity.');
                }
            }

            // Build WHERE argument
            if(!is_null($conditions))
            {
                $conditions = "AND ".CompileConditionString($conditions);
            }

            // Build ORDER BY argument
            if(is_null($order))
            {
                $order = " ORDER by `mikron_order` DESC, `id` DESC";
            }
            else
            {
                $order = " ORDER BY $order";
            }

            if(!is_null($limit))
            {
                $limit = str_replace(" ", null, $limit);
                $tp = explode(",", $limit);
                if(count($tp)!=2)
                {
                    Exception2("Неверное значение аргумента `limit` для функции Query() ожидалось в формате \"число,число\".");
                }
                $limit = " LIMIT $limit";
            }

            $ret = Array();
            if(is_null($aId))
            {
                if($calculate_count_only)
                {
                    $sql = "SELECT count(id) cnt FROM $entityname WHERE (`mikron_state` = 0) $conditions $order $limit";                     
                }
                else
                {
                    $sql = "SELECT $readedfields FROM $entityname WHERE (`mikron_state` = 0) $conditions $order $limit";
                }
            }
            else
            {
                if($aId == '*')
                {
                    if($calculate_count_only)
                    {
                        $sql = "SELECT count(`id`) cnt FROM $entityname WHERE (`mikron_state` = 0) $conditions $order $limit";
                    }
                    else
                    {
                        $sql = "SELECT $readedfields FROM $entityname WHERE (`mikron_state` = 0) $conditions $order $limit";
                    }
                }
                else
                {
                    if($calculate_count_only)
                    {
                        $sql = "SELECT count(`id`) cnt FROM $entityname WHERE (`id` in($aId)) AND (`mikron_state` = 0) $conditions $order $limit";
                    }
                    else
                    {
                        $sql = "SELECT $readedfields FROM $entityname WHERE (`id` in($aId)) AND (`mikron_state` = 0) $conditions $order $limit";
                    }
                }
            }
            return $sql;
        }

        /**
        * Считывает коллекцию сущностей определенного типа по заданным условиям
        * 
        * @param mixed $entityname
        * @param string $aId Id of entity
        * @param mixed $conditions Criterion or Criteria
        * @param mixed $order
        * @param mixed $limit Format: N,N
        * @param mixed $parent
        * @param mixed $calculate_count_only
        * @param mixed $readed_fields
        */
        public function Query($entityname, $aId = null, $conditions = null, $order = null, $limit = null, $parent = null, $calculate_count_only = false, $readed_fields = null)
        {
            $sql = $this->CompileQueryParameters($entityname, $aId, $conditions, $order, $limit, $readed_fields, $calculate_count_only);

            $result = mysql_query($sql);
            if(mysql_errno())
            {
                throw new Exception("Error in file ".__FILE__." on line ".__LINE__.", ".mysql_error());
            }
            $row_fields = Array();
            for($i=0; $i<mysql_numfields($result); $i++)
            {
                $fname = mysql_field_name($result,$i);
                $row_fields[] = $fname;
            }

            $ret = Array();

            while($row = mysql_fetch_object($result))
            {
                if($calculate_count_only)
                {
                    return $row->cnt;
                }
                else
                {
                    $id = $row->id;
                    $ret[] = new $entityname($id, $readed_fields, $parent, $row);
                }
            }
            if($calculate_count_only && is_array($ret))
            {
                $ret = 0;
            }
            return $ret;
        }
        
        public function CloseTransaction()
        {
            if($this->transaction_started)
            {
                mysql_query("COMMIT;");
                $this->transaction_started = false;
            }
        }

        public function Save($entities, $closetransaction = true)
        {
            global $mikron;
            if(!$this->transaction_started)
            {
                // mysql_query("START TRANSACTION;");
                Queries::StartTransaction();
                $this->transaction_started = true;
            }
            try
            {
                foreach($entities as $entity)
                {
                    $entity->Save();
                }
                if($closetransaction)
                {
                    // mysql_query("COMMIT;");
                    Queries::CommitTransaction();
                    $this->transaction_started = false;
                }
            }
            catch(Exception $ex)
            {
                // mysql_query("ROLLBACK;");
                Queries::RollbackTransaction();
                $this->transaction_started = false;
                throw new Exception($ex->getMessage());
                // CriticalError("Ошибка в файле ".__FILE__." в строке ".__LINE__.", ".$ex->getMessage());
            }
        }

        public function ShowList($entities, $editable = true, $deletable = true,
                                    $restorable = false, $reverse = true, $forusers = false,
                                    $included = false,
                                    $pageindex = 0, $pages = 1,
                                    $tableClass = 'table3', $selrowcolor = '#5a5a5a')
        {
            try
            {
                if($deletable)
                {
                    echo "{{superliteral}}";
                }
                $this->ShowListFunc($entities, $editable, $deletable, $restorable, $reverse, $forusers, $included, $pageindex, $pages, $tableClass, $selrowcolor);
            }
            catch(Exception $ex)
            {
                echo $ex->getMessage();
            }
            if($deletable)
            {
                echo "{{/superliteral}}";
            }
        }

        private function ShowListFunc( $entities, $editable = true, $deletable = true,
                                    $restorable = false, $reverse = true, $forusers = false,
                                    $included = false,
                                    $pageindex = 0, $pages = 1,
                                    $tableClass = 'table3',
                                    $selrowcolor = '#5a5a5a')
        {
            global $mikron;
            global $showlistcounter;

            $aEntities = $entities;
            if(is_null($entities)|| !($entities instanceof QueryClass) || ($aEntities->count() < 1))
            {
                ?>Список пуст.<?php
                return;
            }

            $thisFormName = 'grouplistform'.++$showlistcounter;

            $group_operations_enabled = $deletable;
            $allow_order_change = !$forusers;
            if(($editable|| $deletable) && !$included)
            {
                $frm = new html_form($thisFormName, 'post', $_SERVER['REQUEST_URI']);
            }
            $tbl = new html_table(0, $tableClass, null, 0, 0, 'entedtable');

                // запрет редактирования если это лог-записи
                if($aEntities->classname() == 'T_LOGRECORD')
                {
                    $editable = false;
                    $deletable = false;
                    $group_operations_enabled = false;
                }

                // заголовки таблицы служебных аттрибутов сущности
                if($group_operations_enabled)
                {
                    $tbl->addhead();
                    ?><input type="checkbox" onclick="selectall(this.checked, '<?php echo $thisFormName; ?>');" />
                    <script type="text/javascript"><!--
                        function selectall(value, formname)
                        {
                            var items = document.getElementsByName("selecteditems[]");
                            for(i = 0; i < items.length; i++)
                            {
                                items[i].checked = value;
                            }
                            $('#'+formname).get(0).delselitms.disabled = false;
                        }
                        // -->
                    </script><?php
                }
                $tbl->addhead('№');
                if($allow_order_change)
                {
                    $tbl->addhead("Порядок");
                }

                // столбец служебных функций с записью
                if($editable|| $restorable)
                {
                    $tbl->addhead('&nbsp;');
                }

                $entity = $aEntities->fetch();
                $entity_class_name = get_class($entity);
                $aEntities->reset();

                // заголовки таблицы аттрибутов сущности
                foreach($entity->fields as $field)
                {
                    $name = $field['name'];
                    $type = $field['type'];
                    if($forusers)
                    {
                        $hidden = $field['hidden'];
                    }
                    else
                    {
                        $hidden = false;                        
                    }
                    $description = $field['description'];
                    $basetype = getbasetype($type);
                    if(!$hidden)
                    {
                         $tbl->addhead($description);
                    }
                }

                $aEntities_count = $aEntities->count();
                $maxorder = null;
                $minorder = null;
                while($entity = $aEntities->fetch())
                {
                    if(is_null($maxorder))
                    {
                        $entityname = get_class($entity);
                        $r = mysql_query('SELECT max(`mikron_order`) maxorder, min(`mikron_order`) minorder FROM '.$entityname);
                        if(mysql_num_rows($r)>0)
                        {
                            $maxorder = mysql_result($r, 0, 'maxorder');
                            $minorder = mysql_result($r, 0, 'minorder');
                        }
                    }
                    $rowid = 'row'.$entityname.'_'.$entity->id;
                    $tbl->newrow($rowid);
                    if($group_operations_enabled)
                    {
                        $tbl->newcol(null, false, true, $rowid, null);
                        ?><input onclick="opendsi(this.checked, '<?php echo $thisFormName; ?>', '<?php echo $rowid;?>')" type="checkbox" name="selecteditems[]" value="<?php echo $entity->id; ?>" /><?php
                    }
                    $tbl->newcol($entity->id, false, true, $rowid);
                    if($allow_order_change)
                    {
                        $tbl->newcol(null, false, true);
                        $navigate_class = get_class($entity);
                        $supclass = null;
                        if(isset($_GET['edit']) && isset($_GET['entityname']) && isset($_GET['id']))
                        {
                            if($_GET['entityname'] != $navigate_class)
                            {
                                $supid = (int)$_GET['id'];
                                $supclass = '&supclass='.$_GET['entityname'].'&supid='.$supid;
                            }
                        }
                        if($aEntities_count > 1)
                        {
                            if(!$reverse)
                            {
                                if($entity->mikron['order'] > $minorder)
                                {
                                    ?>
                                    <a href="?entityname=<?php echo $navigate_class, $supclass; ?>&amp;changeorder=up&amp;id=<?php echo $entity->id; ?>">&darr;</a><?php
                                }
                                if($entity->mikron['order'] < $maxorder)
                                {
                                    ?><a href="?entityname=<?php echo $navigate_class, $supclass; ?>&amp;changeorder=down&amp;id=<?php echo $entity->id; ?>">&uarr;</a></td><?php
                                }
                            }
                            else
                            {
                                if($entity->mikron['order'] > $minorder)
                                {
                                    ?><a href="?entityname=<?php echo $navigate_class, $supclass; ?>&amp;changeorder=up&amp;id=<?php echo $entity->id; ?>">&uarr;</a><?php
                                }
                                if($entity->mikron['order'] < $maxorder)
                                {
                                    ?><a href="?entityname=<?php echo $navigate_class, $supclass; ?>&amp;changeorder=down&amp;id=<?php echo $entity->id; ?>">&darr;</a></td><?php
                                }
                            }
                        }
                        else
                        {
                            ?>&nbsp;<?php
                        }
                    }

                    if($editable|| $restorable)
                    {
                        $tbl->newcol(null, false, true, null, null, null, "top");
                        /*if($deletable && false) { ?><a onclick="return confirm('Вы хотите удалить элемент?');"href="<?php echo $_SERVER['SCRIPT_NAME']."?delete=&amp;entityname=".get_class($entity)."&amp;id=".$entity->id; ?>">Удалить</a><?php }
                        if($deletable && $editable && false) { ?> <font color="#c0c0c0">|</font> <?php }*/
                        if($editable) { ?><a href="<?php echo $_SERVER['SCRIPT_NAME']."?edit=&amp;entityname=".get_class($entity)."&amp;id=".$entity->id; ?>"><img alt="Ред." title="Редактировать запись" src="<?php echo constant('MIKRON_DIRECTORY_WWW'); ?>/images/buttons/16/file_edit.png" /></a><?php }
                        if($restorable) { ?><a href="<?php echo $_SERVER['SCRIPT_NAME']."?restore=&entityname=".get_class($entity)."&id=".$entity->id; ?>">Восстановить</a><?php }
                    }

                    foreach($entity->fields as $field)
                    {
                        $name = $field['name'];
                        $editMode = $field['editmode'];
                        if($forusers)
                        {
                            $hidden = (int)$field['hidden'];
                        }
                        else
                        {
                            $hidden = false;                        
                        }
                        if(!$hidden)
                        {
                            if($editMode == 'parent')
                            {
                                $tbl->newcol(null, false);
                                try
                                {
                                    $value = null;
                                    $value = $this->getvalue($entity->$name);
                                    if(is_object($value))
                                    {
                                        echo $value->DisplayName();
                                    }
                                    else
                                    {
                                        echo "<font class=\"disabledtext\">null</font>";
                                    }
                                }
                                catch(Exception $ex)
                                {
                                    echo sprintf('<font color="red">%s</font>', $ex->getMessage());
                                }
                                continue;
                            }
                            elseif($editMode=="childattribute")
                            {
                                $tbl->newcol("<font class=\"disabledtext\">массив</font>", true);
                                continue;
                            }
                        }
                        $type = $field['type'];
                        $description = $field['description'];
                        $basetype = getbasetype($type);
                        $value = $this->getvalue($entity->$name);
                        if(!is_object($value))
                        {
                            if(!is_numeric($value))
                            {
                                $value = $mikron->Tools->String->TruncateStringDotted($value, 128, 7, true);
                                if($value == '') 
                                {
                                    $value = '&nbsp;';
                                }
                                else
                                {
                                    if($type == 'datetime')
                                    {
                                        $value = date($mikron->Constants->DateTimeFormat, strtotime($value));
                                    }
                                    $value = htmlspecialchars($value);
                                }
                            }
                        }
                        if($hidden == 0)
                        {
                            switch($basetype)
                            {
                                case "varchar": case "int": case "datetime": case "text": case "file": case "picture": case "html": case "php":
                                    $tbl->newcol($value);
                                    break;
                                case 'boolean':
                                    if(array_key_exists($basetype, $this->Types))
                                    {
                                        $t = $this->Types[$basetype];
                                        $editor = $t['editor'];
                                        if(is_array($editor))
                                        {
                                            foreach($editor as $editor_key => $editor_value)
                                            {
                                                if($value == $editor_value)
                                                {
                                                    $value = $editor_key;
                                                    break;
                                                }
                                            }
                                        }
                                    }
                                    $tbl->newcol($value);
                                    break;
                                case 'password':
                                    if(is_null($value) || ($value == ''))
                                    {
                                        $tbl->newcol('<font class="disabledtext">пусто</font>', true);
                                    }
                                    else
                                    {
                                        $tbl->newcol('<font class="disabledtext">скрыт</font>', true);
                                    }
                                    break;
                                default:
                                    $value2 = '';
                                    if($editMode == 'parent')
                                    {
                                        $tbl->newcol(ConvertToListItem($value));
                                    }
                                    else
                                    {
                                        $enum = $mikron->Enumerators->getEnum($type);
                                        foreach($enum as $item)
                                        {
                                            if($item['value'] == $value)
                                            {
                                                $value2 = $item['description'];
                                                break;
                                            }
                                        }
                                        if($value2 == "") {$value2 = "&nbsp;";}
                                        $tbl->newcol($value2);
                                    }
                                    break;
                            }
                        }
                    }
                }
            $tbl->end();

            if($deletable && !$included)
            {
                ?>
                <div class="bottombuttonspanel">
                    <input type="hidden" name="entityname" value="<?php echo $mikron->Crypt->Encrypt($entity_class_name); ?>" />
                    <input id="delselitms" type="submit" name="groupoperation[deleteselectitems]" value="Удалить выбранные" />
                    <?php if($pages>1)
                    {
                        echo "<div style='padding: 0em; background-color: #444; padding-bottom: .5em; float: right; color: #ccc; '>Страница:";
                        if($pages < 32)
                    {
                        for($i=1; $i<=$pages; $i++)
                        {
                            if($pageindex==$i)
                            {
                                echo " $i ";                                
                            }
                            else
                            {
                                echo " <a href=\"?entityname=$entityname&page=$i\">$i</a> ";
                            }
                        }
                    }
                    else
                    {
                    ?>
                    <select onchange="location.href='?entityname=<?php echo $entityname; ?>&page='+this.options.value;">
                        <?php 
                            for($i=1; $i<=$pages; $i++)
                            {
                                ?><option <?php if($pageindex==$i){?>selected="selected"<?php } ?> value="<?php echo $i; ?>"><?php echo $i; ?></option><?php
                            }
                        ?>
                    </select>
                    <?php } echo "</div>"; } ?>
                </div>
                <script type="text/javascript">
                    $('#<?php echo $thisFormName; ?>').get(0).delselitms.disabled = true;
                    function opendsi(value, formname, row)
                    {
                        if(value)
                        {
                            eval(row).style.backgroundColor = '<?php echo htmlspecialchars($selrowcolor); ?>';
                        }
                        else
                        {
                            eval(row).style.backgroundColor = '';
                        }
                        $('#'+formname).get(0).delselitms.disabled = false;
                    }
                </script>
                <?php
            }
            if(($editable|| $deletable) && !$included)
            {
                $frm->end();
            }
        }
        
        private function getAttributeEditor(& $entity, & $field, $formsign, $require_double_password_field, $inputsize, $textarea_cols, $textarea_rows, $hide_labels = false)
        {
            ob_start();
            $dObj = new DataObject('AttributeEditor');
            $this->getAttributeEditor_Function($dObj, $entity, $field, $formsign, $require_double_password_field, $inputsize, $textarea_cols, $textarea_rows, $hide_labels);
            $dObj->SetString('code', ob_get_clean());
            return $dObj;
        }
        
        private function getAttributeEditor_Function(& $dObj, & $entity, & $field, $formsign, $require_double_password_field = true, $inputsize, $textarea_cols, $textarea_rows, $hide_labels = false)
        {

            global $mikron;

            $name = $field['name'];
            $type = $field['type'];
            $basetype = getbasetype($type);
            $description = $field['description'];
            $help = $field['help'];
            $editMode = $field['editmode'];
            $value = $this->getvalue($entity->$name);
            $error = $entity->$name->error;
            $htmlvalue = null;

            if(!is_numeric($inputsize)){$inputsize = 32;}
            if(!is_numeric($textarea_cols)){$textarea_cols = 64;}
            if(!is_numeric($textarea_rows)){$textarea_rows = 8;}

            if(is_object($value))
            {
                if($value instanceof QueryClass)
                {
                    
                }
                else
                {
                    $htmlvalue = $value->DisplayName();
                }
            }
            elseif(!is_array($value))
            {
                $htmlvalue = is_null($value)?$htmlvalue:htmlspecialchars($value);
            }

            // Calculate colspan for HTML-table
            $colspan = 1;
            if(array_key_exists($basetype, $this->Types))
            {
                $t = $this->Types[$basetype];
                $editor = $t['editor'];
                if(!is_array($editor))
                {
                    $colspan = (int)$t['columns'];
                    if($colspan < 1)
                    {
                        $colspan = 1;
                    }
                }
            }

            $dObj->SetInt('fullrow', $colspan > 1);

            $id_for_label = $name;
            $label = $hide_labels ? null : '<label for="%label_for%">%label_description%</label> %error%<br />';
            if(!$hide_labels)
            {
                $label = str_replace('%label_for%', $id_for_label, $label);
                $label = str_replace('%label_description%', $description, $label);
                $label = str_replace('%error%', "<span class=\"error_font\">$error</span>", $label);
            }

            $attr_editor_name = str_replace('%name%', $name, $formsign);

            if(array_key_exists($basetype, $this->Types))
            {
                echo $label;
                $t = $this->Types[$basetype];
                $editor = $t['editor'];
                if(is_array($editor))
                {
                    html_form::CreateSelectControlForArray($attr_editor_name, $editor, $value);
                }
                else
                {
                    $dbtype = $t['dbtype'];
                    $editor = str_replace('%name%', $attr_editor_name, $editor);
                    $editor = str_replace('%labelname%', $id_for_label, $editor);
                    $editor = str_replace('%value%', htmlspecialchars($value), $editor);
                    $editor = str_replace('%help%', "<span class=\"help_text\">$help</span>", $editor);
                    $editor = str_replace('%htmlvalue%', $htmlvalue, $editor);
                    $editor = str_replace('%inputsize%', $inputsize, $editor);
                    $editor = str_replace('%textarea_cols%', $textarea_cols, $editor);
                    $editor = str_replace('%textarea_rows%', $textarea_rows, $editor);
                    if($basetype == 'password')
                    {
                        if($require_double_password_field)
                        {
                            $editor = str_replace('%repass%', $t['extend_form'], $editor);
                        }
                        else
                        {
                            $editor = str_replace('%repass%', null, $editor);
                        }
                    }
                    echo $editor;
                }
                if(($basetype == 'picture') && ($value != ''))
                {
                    ?><a href="<?php echo $value; ?>">Посмотреть изображение</a><?php
                }
                switch($basetype)
                {
                    case 'picture': case 'file':
                    {
                        if($value != '')
                        {
                            $nullname = "settonull[$name]";
                            $nullname2 = 'settonull_'.$name;
                            ?>
                            <div>
                                <input id="<?php echo $nullname2; ?>" name="<?php echo $nullname; ?>" type="checkbox" />
                                <label for="<?php echo $nullname2; ?>">Удалить файл</label>
                            </div>
                            <?php
                        }
                        break;
                    }
                }
            }
            elseif($editMode == 'select')
            {
                echo $label;
                $enum = $mikron->Enumerators->getEnum($type);
                html_form::CreateSelectControlForEnumerator($attr_editor_name, $enum, $value);
            }
            elseif($editMode == 'parent')
            {
                echo $label;
                html_form::CreateSelectControlForEntity($attr_editor_name, $type, $value);
            }
            elseif($editMode == 'childattribute')
            {
                $dObj->SetInt('fullrow', true);
                echo $label;
                $this->ShowList($value, false, false, false, false, true);
            }
        }
        
        public function CreateFormValidator(MikronFormDescription $formDescription)
        {
            // print_r($formDescription);
        }

        /**
        * put your comment there...
        * 
        * @param object $entity
        * @param string $defaulttitle
        * @param bool $private
        * @param bool $captcha
        * @param string $returntopage
        * @param bool $showchilds
        * @param string $processor_uri
        * @param string $onsubmit
        * @param string $formname
        * @param string $class
        * @param string $formid
        * @param string $error_page
        * @param string $ok_button_caption
        * @param int $columns Количество столбцов в табличной верстве формы
        * @param string $button_caption
        * @param mixed $width
        * @param mixed $formsign
        * @param bool $require_double_password_field Нужен или нет повторный ввод пароля
        * @param int $inputsize
        * @param int $textarea_cols
        * @param int $textarea_rows
        * @param int $cellspacing
        * @param int $cellpadding
        * @param string $htmlform
        * @param bool $hide_labels
        */
        public function ShowFormForUsers($entity,
            $defaulttitle = true,
            $private = false,
            $captcha = false,
            $returntopage = null,
            $showchilds = false,
            $processor_uri = null,
            $onsubmit = null,
            $formname = null,
            $class = null,
            $formid = null,
            $error_page = null,
            $ok_button_caption = null,
            $columns = 2,
            $button_caption = null,
            $width = 240,
            $formsign = null,
            $require_double_password_field = true,
            $inputsize = null,
            $textarea_cols = null,
            $textarea_rows = null,
            $cellspacing = null,
            $cellpadding = null,
            $htmlform = null, 
            $hide_labels = false)
        {

            global $mikron;
            
            $formkey = md5(MikronTimer::getmicrotime() + mt_rand(0, 9999999999));
            $formDescription = new MikronFormDescription($formkey);

            if(is_null($onsubmit))
            {
                $onsubmit = 'return validate_fields(this, form_validators_'.$formkey.');';
            }
            else
            {
                $onsubmit = sprintf('if(validate_fields(this, form_validators_%s)){%s}else{return false;}', $formkey, $onsubmit);
            }

            if(!is_numeric($cellspacing)){$cellspacing = 4;}
            if(!is_numeric($cellpadding)){$cellpadding = 4;}

            if(is_null($formsign))
            {
                $formsign = 'userform';
            }

            if(!in_array($formsign, Array('userform', 'entity')))
            {
                try
                {
                    $result = $mikron->EntityManager->CheckSave($formsign);
                    if(is_object($result))
                    {
                        if(!is_null($returntopage))
                        {
                            $returntopage = str_replace('%ID%', $result->id, $returntopage);
                            $returntopage = $mikron->Tools->MakePath($returntopage);
                            $mikron->Tools->ThrowPage(null, 200, $returntopage);
                        }
                    }
                }
                catch(Exception $ex)
                {
                    $button_caption = null;
                    if(is_object($_SESSION['ErrorItem']))
                    {
                        $error_object = &$_SESSION['ErrorItem'];
                        if(is_object($entity))
                        {
                            if(get_class($entity) == get_class($error_object))
                            {
                                $entity = $error_object;
                            }
                        }
                    }
                    else
                    {
                        echo '{{nicewindow style=error caption="Внимание!" width=240}}', $ex->getMessage(), '{{/nicewindow}}';
                    }
                }
            }
            
            $encodekey = md5(time());
            $show_button_only = !is_null($button_caption);
            $id = $entity->id;
            $isnew = $entity->isnew;
            $action = is_null($processor_uri)?$_SERVER['REQUEST_URI']:$processor_uri;
            $formid = strip_tags($formid);
            $formname = strip_tags($formname);
            $unique_tab_id = 'frm'.substr(md5(microtime()), 0, 8).mt_rand(0, 999999);
            $ok_button_caption = is_null($ok_button_caption)?($isnew?'Добавить':'Сохранить'):$ok_button_caption;
            if(!$isnew)
            {
                ?>{{superliteral}}<?php
            }

            if($show_button_only)
            {
                echo sprintf('<button onclick="javascript: %s.style.display = \'\'; this.style.display=\'none\';">%s</button>', $unique_tab_id, $button_caption);
            }

            $properties = Array();
            // if(!$mikron->Tools->IsNullOrEmpty($formname)) {$properties[] = sprintf('name="%s"', $formname);}
            if(!$mikron->Tools->IsNullOrEmpty($formid)) {$properties[] = sprintf('id="%s"', $formid);}
            if(!$mikron->Tools->IsNullOrEmpty($class)) {$properties[] = sprintf('class="%s"', $class);}
            if(!$mikron->Tools->IsNullOrEmpty($onsubmit)) {$properties[] = sprintf('onsubmit="%s"', $onsubmit);}
            if(!$mikron->Tools->IsNullOrEmpty($action)) {$properties[] = sprintf('action="%s"', $action);}
            if(!$isnew) {$properties[] = sprintf('id="%s"', 'mikron+Form');}
            $properties[] = 'enctype="multipart/form-data"';
            $properties[] = 'method="post"';
            $properties[] = 'style="margin: 0px; width: 100%;"';
            
            $hidden_fields = Array();
            $hidden_fields['MAX_FILE_SIZE'] = 10485760;
            $hidden_fields[$formsign.'[classname]'] = get_class($entity);
            $hidden_fields[$formsign.'[classname2]'] = md5("ecn_$encodekey".get_class($entity));
            $hidden_fields[$formsign.'[encodekey]'] = $encodekey;
            $hidden_fields[$formsign.'[_returntopage]'] = $returntopage;
            $hidden_fields[$formsign.'[_errorpage]'] = $error_page;
            $hidden_fields['saveentityform'] = 'true';
            if($private) {$hidden_fields['flag_privateform'] = 'true';}
            if(!$isnew) {$hidden_fields[$formsign.'[id]'] = $id;}
            
            $basic_editors = Array();
            $hidden_editors = Array();

                    // определение выводимых групп
                    $groups = Array();
                    foreach($entity->fields() as $field)
                    {
                        $hidden = $field['hidden'];
                        $name = $field['name'];
                        if($hidden)
                        {
                            $type = $field['type'];
                            $basetype = getbasetype($type);
                            $super_hidden = !in_array($basetype, Array('int', 'varchar', 'bool'));
                            if(!$super_hidden)
                            {
                                $t = $this->Types[$basetype];
                                $editor = $t['editor'];
                                if(!is_array($editor))
                                {
                                    $value = $mikron->Crypt->Encrypt($this->getvalue($entity->$name), $encodekey);
                                    $dbtype = $t['dbtype'];
                                    $editor = str_replace("%name%", $formsign."[$name]", $editor);
                                    $editor = str_replace(' id="%labelname%"', null, $editor);
                                    $editor = str_replace(' maxlength="%length%"', null, $editor);
                                    $editor = str_replace("%value%", htmlspecialchars($value), $editor);
                                    if($basetype == 'password')
                                    {
                                        $editor = str_replace('%repass%', null);
                                    }
                                    $hidden_editors[$name] = $editor;
                                    // echo "<div style=\"display: none;\">$editor</div>";
                                }
                            }
                        }
                        $formDescription->addField($name, $hidden);
                        // проверяем не скрытое ли поле
                        if(!$hidden)
                        {
                            $groupname = $field['group'];
                            if(!array_key_exists($groupname, $groups))
                            {
                                $groups[$groupname] = Array();
                            }
                            $groups[$groupname][] = $field;
                        }
                    }
                    
                    $validators = Array();
                    // вывод с группировкой по группам
                    foreach($groups as $groupname => $elements)
                    {
                        /*if(!$mikron->Tools->IsNullOrEmpty($groupname))
                        {
                            if(count($elements) > 0)
                            {
                                $tbl->newrow();
                                $tbl->newcol($groupname, false, false, null, 'userformgroupcell', null, 'middle', null, 2);
                            }
                        }*/
                        foreach($elements as $field)
                        {
                            $validator = $field['validator'];
                            $description = $field['description'];
                            $name = $field['name'];
                            if(!$mikron->Tools->IsNullOrEmpty($validator))
                            {
                                $validator_key = sprintf('%s[%s]', $formsign, $name);
                                $validator = 'fieldname: \''.$description.'\',' . $validator;
                                $validator = trim($validator);
                                if(substr($validator, -1, 1) == ',')
                                {
                                    $validator = substr($validator, 0, strlen($validator)-1);
                                }
                                $validators[$validator_key] = $validator;
                            }
                            $editor = $this->getAttributeEditor($entity, $field, $formsign.'[%name%]', $require_double_password_field, $inputsize, $textarea_cols, $textarea_rows, $hide_labels);
                            $editor_code = $editor->getString('code');
                            $desc = $formDescription->getField($name);
                            $editor_code = str_replace('%id%', $desc->getID(), $editor_code);
                            /*
                            $fullrow = $editor->getInt('fullrow');
                            if($fullrow)
                            {
                                $tbl->newrow();
                            }
                            $columns_count = $fullrow?$columns:1;
                            $tbl->newcol($editor_code, false, null, null, null, null, 'top', null, $columns_count);
                            */
                            $basic_editors[$field['name']] = $editor_code;
                        }
                    }

            $style = $show_button_only?' style="display: none;"':null;
            ?>
            <form <?php echo implode(' ', $properties); ?>>
            <div id="addeditform" style="padding: 0px; margni: 0px; ">
                <?php

                    $replacements = Array();
                    $template = '%mikron_editor_form_system_area%';
                    $template .= $htmlform;
                    foreach($hidden_editors as $key => $value)
                    {
                        if(is_null($htmlform)) {$template .= sprintf('<div style="display: none; ">%s</div>', '%'.$key.'%');}
                        $replacements[$key] = $value;
                    }
                    if(is_null($htmlform))
                    {
                        $template .= '<table id="%mikron_editor_table_id%" style="%mikron_editor_table_style%" border="0" cellspacing="%mikron_editor_table_cellspacing%" cellpadding="%mikron_editor_table_cellpadding%">
                            <tr>
                                <td><h1>%mikron_editor_form_caption%</h1></td>
                            </tr>';
                    }
                    foreach($basic_editors as $key => $value)
                    {
                        if(is_null($htmlform)) {$template .= sprintf('<tr><td>%s</td></tr>', '%'.$key.'%');}
                        $replacements[$key] = $value;
                    }
                    if($captcha)
                    {
                        if(is_null($htmlform)) {$template .= '<tr><td>%mikron_editor_form_captcha%</td></tr>';}
                    }
                    if(is_null($htmlform)) {$template .= '<tr><td>%mikron_editor_form_submit_button%</td></tr></table>';}

                    $replacements['mikron_editor_form_caption'] = htmlspecialchars($defaulttitle);
                    $replacements['mikron_editor_table_id'] = $unique_tab_id;
                    $replacements['mikron_editor_table_style'] = $style;
                    $replacements['mikron_editor_table_cellspacing'] = $cellspacing;
                    $replacements['mikron_editor_table_cellpadding'] = $cellpadding;

                    $mikron_editor_form_system_area = null;
                    foreach($hidden_fields as $key => $value)
                    {
                        $mikron_editor_form_system_area .= sprintf('<input type="hidden" name="%s" value="%s" />', $key, $value)."\r\n";
                    }
                    $replacements['mikron_editor_form_system_area'] = $mikron_editor_form_system_area;
                    if($captcha)
                    {
                        ob_start();
                        $t2 = new html_table(2, 'captcha_table', 1, 0, 0);
                        $t2->newcol();
                            echo '<img src="/mikron/captcha/captcha.png?rgb=238_238_238" alt="Код подтверждения" /> ';
                        $t2->newcol();
                            ?><input name="captcha" size="5" maxlength="5" /><?php
                        $t2->newcol('<span class="help_text">* Введите код подтверждения указанный на картинке.</span>', true, null, null, null, null, 'top', null, 2);
                        $t2->end();
                        $captcha_editor = ob_get_clean();
                        $replacements['mikron_editor_form_captcha'] = $captcha_editor;
                    }
                    $replacements['mikron_editor_form_submit_button'] = sprintf('<input type="submit" name="save" value="%s" />', $ok_button_caption);                   
                    $current_template = $template;
                    /* Замена инструкций в форме */
                    foreach($replacements as $key => $replacement)
                    {
                        $current_template = str_replace('%'.$key.'%', $replacement, $current_template);
                    }
                    echo $current_template;
                    ?>
                    <script type="text/javascript">
                        var form_validators_<?php echo $formkey; ?> = {
                        <?php
                        $validators_array = Array();
                        foreach($validators as $key => $value)
                        {
                            $validators_array[] = sprintf('\'%s\': {%s}', $key, $value);
                        }
                        echo implode(',', $validators_array);
                        ?>};
                    </script>
            </div>
            </form>
            <?php
            return $formDescription;
            if(!$isnew)
            {
                ?>{{/superliteral}}<?php
            }
        }

        public function ShowForm($entity, $defaulttitle = true, $private = false, $returntopage = null)
        {
            echo '{{superliteral}}';
            try
            {
                $this->ShowForm_function($entity, $defaulttitle, $private, $returntopage);
            }
            catch(Exception $ex)
            {
                CriticalError($ex->getMessage(), $ex, true);
            }
            echo '{{/superliteral}}';
        }
        
        function ShowForm_function($entity, $defaulttitle = true, $private = false, $returntopage = null)
        {
            global $mikron;
            static $forms_count = 0;
            $id = $entity->id;
            $isnew = $entity->isNew;
            ?>
            <form onkeypress="ctrlEnter(event, this);" <?php echo $isnew?"":" id=\"mikron+Form\""; ?>
                    enctype="multipart/form-data"
                    action="<?php echo $_SERVER['REQUEST_URI']; ?>"
                    method="post"
                    style="margin-bottom: 1em; ">
                <div>
                    <?php
                        if(!$isnew)
                        {
                            ?><input type="hidden" name="entity[id]" value="<?php echo $id; ?>" /><?php
                            echo "\r\n";
                        }
                        $encodekey = md5(time());
                    ?>
                    <input type="hidden" name="MAX_FILE_SIZE" value="10485760" />
                    <?php if($private){ ?><input type="hidden" name="flag_privateform" value="true" /><?php echo "\r\n"; } ?>
                    <input type="hidden" name="entity[classname]" value="<?php echo get_class($entity); ?>" />
                    <input type="hidden" name="entity[classname2]" value="<?php echo md5("ecn_$encodekey".get_class($entity)); ?>" />
                    <input type="hidden" name="entity[encodekey]" value="<?php echo $encodekey; ?>" />
                    <input type="hidden" name="entity[_returntopage]" value="<?php echo $returntopage; ?>" />
                    <input type="hidden" name="saveentityform" value="true" />
                </div>
            <?php

            if($defaulttitle)
            {
                if(!$isnew)
                {
                    ?><h2>Редактирование элемента «<?php echo $entity->description(); ?>»</h2><?php
                }
            }

            $forms_count++;
            $addedformid = 'addedform_'.$forms_count;
            $addedbuttonid = 'addedbutton_'.$forms_count;

            if($isnew)
            {
                ?>
                <div class="mikronform" id="<?php echo $addedformid; ?>" style="display: none; position: relative; left: -64px; top: -196px; ">
                <script type="text/javascript">
                    $().ready(
                    function()
                    {
                      $('#<?php echo $addedformid; ?>').jqm({overlay: 75, modal: true, trigger: false});
                    });
                </script>
                <div class="mikronform_box">
                <div class="mikronform_closebutton" onclick="javascript: $.mikron.window('#<?php echo $addedformid;?>').triggerModal();">Закрыть</div>
                <?php
            }

            if($defaulttitle)
            {
                if($isnew)
                {
                    ?><h2>Добавление элемента «<?php echo $entity->description(); ?>»</h2><?php
                }
            }

            ?>
            <table border="0" class="table1">
                <?php
                    foreach($entity->fields as $field)
                    {
                        $hidden = false; //$field['hidden'];
                        if(!$hidden)
                        {
                            $name = $field['name'];
                            $type = $field['type'];
                            $basetype = getbasetype($type);
                            $description = $field['description'];
                            $help = $field['help'];
                            $childattribute = $field['childattribute'];
                            $editMode = $field['editmode'];
                            $parenterror = null;
                                try
                                {
                                    $value = $this->getvalue($entity->$name);
                                }
                                catch(Exception $ex)
                                {
                                    $parenterror = sprintf('<font color="red">%s</font>', $ex->getMessage());
                                    $value = null;
                                }
                                if(is_object($value)|| is_array($value))
                                {
                                    $htmlvalue = null;                                                            
                                }
                                else
                                {
                                    $htmlvalue = htmlspecialchars($value);                                
                                }

                                $id_for_lable = "entity_$name";
                                ?><tr>
                                <td valign="top" style="vertical-align: top; ">
                                <label for="<?php echo $id_for_lable; ?>"><?php echo $description; ?></label>:
                                </td><td><?php
                                if(array_key_exists($basetype, $this->Types))
                                {
                                    $t = $this->Types[$basetype];
                                    $editor = $t['editor'];
                                    
                                    if(is_array($editor))
                                    {
                                        ?><select name="entity[<?php echo $name; ?>]"><?php
                                        foreach($editor as $editor_key => $editor_value)
                                        {
                                            ?><option <?php if($value == $editor_value){echo 'selected="selected"';} ?> value="<?php echo $editor_value; ?>"><?php echo $editor_key; ?></option><?php
                                        }
                                        ?></select><?php
                                    }
                                    else
                                    {
                                        $dbtype = $t['dbtype'];
                                        $dObj = new DataObject(null);
                                        //$editor = $this->getAttributeEditor($entity, $field, 'entity', true, null, null, null);
                                        //$editor = $editor->GetString('code');
                                        $editor = str_replace("%name%", "entity[$name]", $editor);
                                        $editor = str_replace("%labelname%", $id_for_lable, $editor);
                                        $editor = str_replace("%value%", htmlspecialchars($value), $editor);
                                        $editor = str_replace("%help%", "<span class=\"help_text\">$help</span>", $editor);
                                        $editor = str_replace("%htmlvalue%", $htmlvalue, $editor);
                                        $editor = str_replace("%inputsize%", 28, $editor);
                                        $editor = str_replace("%textarea_cols%", 128, $editor);
                                        $editor = str_replace("%textarea_rows%", 16, $editor);
                                        echo $editor;
                                    }
                                    if(($basetype == 'picture') && !$mikron->Tools->IsNullOrEmpty($value))
                                    {
                                        $value_file = constant('MIKRON_DIRECTORY').'/..'.$value;
                                        list($width, $height, $type, $attr) = getimagesize($value_file);
                                        $type_name = null;
                                        switch($type)
                                        {
                                            case 1: $type_name = 'GIF'; break;
                                            case 2: $type_name = 'JPG'; break;
                                            case 3: $type_name = 'PNG'; break;
                                            case 4: $type_name = 'SWF'; break;
                                            case 5: $type_name = 'PSD'; break;
                                            case 6: $type_name = 'BMP'; break;
                                            case 7: $type_name = 'TIFF(intel byte order)'; break;
                                            case 8: $type_name = 'TIFF(motorola byte order)'; break;
                                            case 9: $type_name = 'JPC'; break;
                                            case 10: $type_name = 'JP2'; break;
                                            case 11: $type_name = 'JPX'; break;
                                            case 12: $type_name = 'JB2'; break;
                                            case 13: $type_name = 'SWC'; break;
                                            case 14: $type_name = 'IFF'; break;
                                            case 15: $type_name = 'WBMP'; break;
                                            case 16: $type_name = 'XBM'; break;
                                        }
                                        $image_title = $type_name.' — '.$width.' x '.$height;
                                        echo sprintf('<a href="%s" title="%s">Посмотреть изображение</a>', $value, $image_title);
                                    }
                                    switch($basetype)
                                    {
                                        case "picture": case "file":
                                        {
                                            if($value != "")
                                            {
                                                $nullname = "settonull[$name]";
                                                $nullname2 = "settonull_$name";
                                                ?>
                                                <div>
                                                    <input id="<?php echo $nullname2; ?>" name="<?php echo $nullname; ?>" type="checkbox" />
                                                    <label for="<?php echo $nullname2; ?>">Удалить файл</label>
                                                </div>
                                                <?php
                                            }
                                            break;
                                        }
                                    }
                                }
                                elseif($editMode == 'select')
                                {
                                    $enum = $mikron->Enumerators->getEnum($type);
                                    ?><select name="entity[<?php echo $name; ?>]" id="<?php echo $id_for_lable; ?>"><?php
                                        foreach($enum as $item)
                                        {
                                            ?><option <?php if($item['value'] == $value) {echo 'selected="selected"';} ?> value="<?php echo $item['value'];?>"><?php echo $item['description'];?></option><?php
                                        }
                                    ?></select><?php
                                }
                                elseif($editMode == 'parent')
                                {
                                    if(!is_null($parenterror))
                                    {
                                        echo $parenterror, '<br />';
                                    }
                                        /*global $mikron;
                                        $mikron->TemplateMaker->smarty->assign('id5', array_keys($dn));
                                        $mikron->TemplateMaker->smarty->assign('names5', array_values($dn));
                                        echo "{{html_options values=\$id5 output=\$names5 selected=1}}";                                    
                                        */
                                        $dn = $this->QueryDisplayNames($type);
                                        /*
                                        $dn = Array();
                                        $c = 0;
                                        foreach($dn1 as $key1 => $value1)
                                        {
                                            $c++;
                                            if($c == 1999)
                                            {break;}
                                            $dn[$key1] = $value1;
                                        }*/
                                    ?>
                                    <select name="entity[<?php echo $name; ?>]" id="<?php echo $id_for_lable; ?>">
                                    <option value=""></option>

                                    <?php
                                        foreach($dn as $item)
                                        {
                                            $item_id = $item['id'];
                                            $item_displayname = $item['displayname'];
                                            $selected = null;
                                            if(!(!is_object($value) || $value->isNew))
                                            {
                                                $selected = $item_id == $value->id ? ' selected="selected"': null;
                                            }
                                            echo "\r\n", sprintf('<option%s value="%s">%s</option>', $selected, $item_id, $item_displayname);
                                        }
                                    ?></select><?php
                                }
                                elseif($editMode == "childattribute")
                                {
                                    $this->ShowList($value, true, false, false, true, false, true);
                                }
                                ?></td></tr><?php
                        }
                    }
                    ?>
                <tr>
                    <td>&nbsp;</td>
                    <td><input type="submit" name="save" value="<?php echo $isnew?"Добавить":"Сохранить"; ?>" /></td>
                </tr>
            </table>
            <?php if($isnew) { ?>
            </div>
            </div>
            <?php } ?>
            </form>
            {{/superliteral}}
            <?php
            if($defaulttitle)
            {
                if($isnew)
                {
                    ?>
                    <button id="<?php echo $addedbuttonid; ?>"
                        style = "padding: .3em;"
                        onclick = "$.mikron.window('#<?php echo $addedformid;?>').triggerModal();">Добавить элемент</button>
                    <?php
                }
            }
        }

        public function ShowServerForm($entity, $title = null, $private = false, $MethodName = null)
        {
            global $mikron;
            $id = $entity->id;
            $isnew = $entity->isNew;
            ?>
            <form<?php echo $isnew?"":" id=\"mikron+Form\""; ?> enctype="multipart/form-data" action="<?php echo $_SERVER['SCRIPT_NAME']; ?>" method="post" style="margin-bottom: 1em; ">
                <div>
                    <?php
                        if(!$isnew)
                        {
                            ?><input type="hidden" name="serverentity[id]" value="<?php echo $id; ?>" /><?php
                            echo "\r\n";
                        }
                        $encodekey = md5(time());
                    ?>
                    <input type="hidden" name="MAX_FILE_SIZE" value="10485760" />
                    <?php if($private){ ?><input type="hidden" name="flag_privateform" value="true" /><?php echo "\r\n"; } ?>
                    <input type="hidden" name="serverentity[classname]" value="<?php echo get_class($entity); ?>" />
                    <input type="hidden" name="serverentity[classname2]" value="<?php echo md5("ecn_$encodekey".get_class($entity)); ?>" />
                    <input type="hidden" name="serverentity[encodekey]" value="<?php echo $encodekey; ?>" />
                    <input type="hidden" name="service[methodname]" value="<?php echo $MethodName; ?>" />
                </div>
            <?php
            if(!is_null($title))
            {
                echo $title;
            }
            ?>
            <div id="addeditform">
            <table border="0" class="table1">
                <?php
                    foreach($entity->fields() as $field)
                    {
                        $name = $field['name'];
                        $type = $field['type'];
                        $basetype = getbasetype($type);
                        $description = $field['description'];
                        $help = $field['help'];
                        $hidden = $field['hidden'];
                        $editMode = $field['editmode'];
                        $value = $this->getvalue($entity->$name);
                        $htmlvalue = htmlspecialchars($value);
                        if(!$hidden)
                        {
                            $id_for_lable = "entity_$name";
                            ?><tr>
                            <td valign="top">
                            <label for="<?php echo $id_for_lable; ?>"><?php echo $description; ?></label>:
                            </td><td><?php
                            if(array_key_exists($basetype, $this->Types))
                            {
                                $t = $this->Types[$basetype];
                                $editor = $t['editor'];
                                
                                if(is_array($editor))
                                {
                                    ?><select name="serverentity[<?php echo $name; ?>]"><?php
                                    foreach($editor as $editor_key => $editor_value)
                                    {
                                        ?><option <?php if($value==$editor_value){echo 'selected="selected"';} ?> value="<?php echo $editor_value; ?>"><?php echo $editor_key; ?></option><?php
                                    }
                                    ?></select><?php
                                }
                                else
                                {
                                    $dbtype = $t['dbtype'];
                                    $editor = str_replace("%name%", "serverentity[$name]", $editor);
                                    $editor = str_replace("%labelname%", $id_for_lable, $editor);
                                    $editor = str_replace("%value%", htmlspecialchars($value), $editor);
                                    $editor = str_replace("%help%", "<span class=\"help_text\">$help</span>", $editor);
                                    $editor = str_replace("%htmlvalue%", $htmlvalue, $editor);
                                    echo $editor;
                                }
                                if(($basetype == "picture") && ($value != ""))
                                {
                                    ?>
                                    <a href="<?php echo $value; ?>">Посмотреть изображение</a>
                                    <?php
                                }
                                switch($basetype)
                                {
                                    case "picture": case "file":
                                    {
                                        if($value != "")
                                        {
                                            $nullname = "settonull[$name]";
                                            $nullname2 = "settonull_$name";
                                            ?>
                                            <div>
                                                <input id="<?php echo $nullname2; ?>" name="<?php echo $nullname; ?>" type="checkbox">
                                                <label for="<?php echo $nullname2; ?>">Удалить файл</label>
                                            </div>
                                            <?php
                                        }
                                        break;
                                    }
                                }
                            }
                            elseif($editMode == "select")
                            {
                                $enum = $mikron->Enumerators->getEnum($type);
                                ?><select name="serverentity[<?php echo $name; ?>]" id="<?php echo $id_for_lable; ?>"><?php
                                    foreach($enum as $item)
                                    {
                                        ?><option <?php if($item['value'] == $value) {echo 'selected="selected"';} ?> value="<?php echo $item['value'];?>"><?php echo $item['description'];?></option><?php
                                    }
                                ?></select><?php
                            }
                            elseif($editMode == "parent")
                            {
                                $entities = $this->Query($type);
                                ?><select name="serverentity[<?php echo $name; ?>]" id="<?php echo $id_for_lable; ?>">
                                <option value=""></option>
                                <?php
                                    foreach($entities as $item)
                                    {
                                        ?><option <?php if((!$value->isNew) && ($item->id == $value->id)) {echo 'selected="selected"';} ?> value="<?php echo $item->id;?>"><?php echo ConvertToListItem($item);?></option><?php
                                    }
                                ?></select><?php
                            }
                            ?></td></tr><?php
                        }
                    }
                    ?>
                <tr>
                    <td>&nbsp;</td>
                    <td><input type="submit" name="save" value="<?php echo $isnew?"Ok":"Сохранить"; ?>" /></td>
                </tr>
            </table>
            </div>
            </form>            
            <?php
        }

        // Вызывается после обработки всех сущностей из файла схемы
        public function UpdateEntitiesList()
        {
            if(file_exists($this->entityfile_update))
            {
                if (!copy($this->entityfile_update, $this->entityfile))
                {
                    $err = error_get_last();
                    Exception2("failed to copy $file...\n".$err['message']);
                }
            }
            return true;
        }

        // Создание таблицы для сущности с одноименным названием
        // с добавлением необходимых полей или изменение структуры имеющейся
        // на основе обновленных сведений о сущности
        function add(sheme_entity $oEntity, & $shemeEntities, & $schemaEnums)
        {
            global $mikron;

            // обязательные поля каждой сущности
            $a_def_attributes = Array('mikron_displayname' => 'mikron_displayname varchar(64)');
            // Здравствуйте, спасибо за такой хороший ресурс.
            
            $entityname = $oEntity->name;
            
            foreach($a_def_attributes as $da)
            {
                @mysql_query("ALTER TABLE `$entityname` ADD COLUMN $da");
            }

            $entitydescription = $oEntity->description;
            $templates = $oEntity->template; // Array
            $inmenu = $oEntity->inmenu;
            $displayname = $oEntity->displayname;
            $fields = $oEntity->fields;
            $childs = $oEntity->childs;
            $service_fields = Array('id', 'classname', 'mikron_state', 'mikron_order', 'fields', 'mikron_displayname');
            switch($entityname)
            {
                case 'T_BASE':
                Exception2('В схеме использовано зарезервированное имя сущности T_BASE');
                break;
            }
            $entityname2 = quote_smart($entityname);
            $inmenu2 = quote_smart($inmenu);
            $displayname2 = is_null($oEntity->displayname) ? 'NULL' : quote_smart($oEntity->displayname);
            $entitydescription2 = quote_smart($entitydescription);
            mysql_query('DELETE FROM ENTITIES WHERE `name` = '.$entityname2);
            mysql_query("INSERT INTO ENTITIES(`name`, `inmenu`, `title`, `displayname`) VALUES($entityname2, $inmenu2, $entitydescription2, $displayname2)") OR die(mysql_error());
            // удаление шаблонов сущностей
            mysql_query("DELETE FROM T_ENTITY_TEMPLATE WHERE `entityname` = $entityname2");
            $template_methods = $mikron->Enumerators->getEnum('TEMPLATE_METHOD');
            if(is_array($templates))
            {
                foreach($templates as $tsf)
                {
                    if((!is_null($tsf['template'])) && ($tsf['template'] != ''))
                    {
                        // добавление шаблонов сущностей
                        $condition = $tsf['condition'];
                        $for = $tsf['for'];
                        if(!is_null($for))
                        {
                            if(array_key_exists($for, $template_methods))
                            {
                                $for2 = $template_methods[$for]['value'];
                                $template2 = quote_smart($tsf["template"]);
                                $condition2 = is_null($condition)?"NULL":quote_smart($condition);
                                mysql_query("INSERT INTO T_ENTITY_TEMPLATE(entityname, entitycondition, template, formethod) VALUES($entityname2,$condition2, $template2, $for2)");
                            }
                        }
                    }
                }
            }
            if(is_array($fields))
            {
                // Проверка наличия полей с зарезервированными именами
                $f = Array();
                foreach($fields as $field)
                {
                    $fieldname = $field->name;
                    $f[$fieldname] = $fieldname;
                }
                if(in_array($service_fields, $f))
                {
                    Exception2("В описании сущности $entityname присутствует поле
                    с запрещенным именем $name. Сущность не была создана.");
                }          
                // Создание таблицы ассоциированной с сущностью
                $sql = "CREATE TABLE IF NOT EXISTS `$entityname`
                    (
                        id int(11) unsigned not null AUTO_INCREMENT PRIMARY KEY, 
                        mikron_state int(11) unsigned not null DEFAULT 0,
                        mikron_order int(11) unsigned not null DEFAULT 0,
                        mikron_displayname varchar(64)
                    ) ENGINE=InnoDB DEFAULT CHARSET = utf8 COLLATE = utf8_general_ci";
                @mysql_query($sql) or Exception2("Ошибка создания таблицы для сущности `$entityname`, ошибка: ".mysql_error());
                $enum_fields = Array();
                // Считывание имеющихся полей у таблицы сущности
                $r = @mysql_query("SHOW COLUMNS FROM `$entityname`") or Exception2("Ошибка чтения информации о таблице `$entityname`");
                $exists_fields = Array();
                // пробегаемся по уже имеющимся полям таблицы сущности
                while($row = mysql_fetch_object($r))
                {
                    $fieldname = $row->Field;
                    // если поля нет в схеме, и это не служебное поле, тогда удаляем его у таблицы
                    if(array_key_exists($fieldname, $f))
                    {
                        // добавляем его к списку имеющихся полей
                        $exists_fields[$fieldname] = Array('Type'=>$row->Type, 'Null'=> strtoupper($row->Null)=="YES", 'Default'=>$row->Default, 'Extra'=>$row->Extra);
                    }
                    elseif(!in_array($fieldname, $service_fields))
                    {
                        try
                        {
                            // удаление связанного с полем индекса
                            @mysql_query("ALTER TABLE $entityname DROP INDEX uidx_$fieldname");
                            // удаление поля
                            @mysql_query("ALTER TABLE `$entityname` DROP COLUMN `$fieldname`") or Exception2("Ошибка удаления поля $fieldname из таблицы $entityname<div>".mysql_error()."</div>");
                        }
                        catch(Exception $ex)
                        {
                            //echo in_array($fieldname, $service_fields)?1:0;
                            //exit;
                        }
                    }
                }
                foreach($childs as $child)
                {
                    // cut($child);
                }
                reset($fields);
                $fields_ex = Array();               
                
                    $indexes = Array();
                    foreach($fields as $field)
                    {
                        $name = $field->name; // имя поля указанное в схеме
                        $unique = !is_null($field->unique); // уникальность на таблицу
                        if(!is_null($field->unique) && ($field->unique!=""))
                        {
                            $indexes["uidx_".$field->unique][] = $name;
                        }
                        $defaultvalue = is_null($field->default)?null:$field->default; // значение поля по умолчанию
                        $type = $field->type; // указанный в схеме тип
                        $basetype = getbasetype($type); // тип без указания длины
                        $length = getbaseLength($type); // длина
                        $childattribute = $field->childattribute; // это чайлдлист 
                        // Базовые системные поля сущности
                        $field_vars = get_object_vars($field);
                        if($childattribute)
                        {
                            foreach($shemeEntities as $oEnt_item)
                            {
                                if($oEnt_item->name == $basetype)
                                {
                                    $dbtype = null;
                                    $editMode = "childattribute";
                                    $field_vars['editmode'] = $editMode;
                                    $field_vars['dbtype'] = $dbtype;
                                    $fields_ex[] = $field_vars;
                                    break;
                                }
                            }
                        }
                        else
                        {

                            // Если поле базового системного типа
                            if(array_key_exists($basetype, $this->Types))
                            {
                                $mikron_type = $this->Types[$basetype];
                                $dbtype = $mikron_type['dbtype'];
                                $dbtype = str_replace("%length%", $length, $dbtype);
                                $editMode = $mikron_type['editmode'];
                            }
                            else
                            {
                                $defined_type = false;
                                // Если поле заполняется значением из справочника
                                // 24-04-2009
                                if(isset($schemaEnums[$basetype]))
                                {
                                    $dbtype = "int";
                                    $editMode = "select";
                                    $defined_type = true;      
                                }
                                /*
                                ob_clean();
                                print_r($schemaEnums);
                                exit;
                                $enum = $mikron->Enumerators->getEnum($basetype);
                                if($enum != null)
                                {
                                    $dbtype = "int";
                                    $editMode = "select";
                                    $defined_type = true;                            
                                }*/
                                if(!$defined_type)
                                {
                                    foreach($shemeEntities as $oEnt_item)
                                    {
                                        if($oEnt_item->name == $basetype)
                                        {
                                            $dbtype = 'int';
                                            $editMode = 'parent';
                                            $defined_type = true;
                                            break;
                                        }
                                    }
                                }
                                if(!$defined_type)
                                {
                                    Exception2("В описании сущности $entityname у поля $name
                                    указан несуществующий тип данных «".$basetype."».");
                                    return;
                                }
                            }
                            $column = "`$name` $dbtype";
                            $null = true;
                            $default = null;
                            $extra = null;
                            // Type, Null, Default, Extra
                            if(!is_null($defaultvalue))
                            {
                                switch($dbtype)
                                {
                                    case 'int':
                                        if(!is_numeric($defaultvalue))
                                        {
                                            if($editMode != 'parent')
                                            {
                                                Exception2("В описании сущности $entityname у $name
                                                значение по умолчанию должно быть числового типа.");
                                                return;
                                            }
                                        }
                                        $default = $defaultvalue;
                                        break;
                                    case 'datetime':
                                        /*Exception2("В описании сущности
                                        $entityname поле $name типа $dbtype
                                        невозможно присвоить значение по умолчанию.");
                                        return;*/
                                        break;
                                    default:
                                        $default = $defaultvalue;
                                        break;
                                }
                            }
                            if(!is_null($default) && (substr($default, 0, 7) != 'mikron:'))
                            {
                                $column .= " DEFAULT ".quote_smart($default);
                                $null = false;
                            }
                            $field_vars['editmode'] = $editMode;
                            $field_vars['dbtype'] = $dbtype;
                            $fields_ex[] = $field_vars;
                            $cancel = false;
                            if(array_key_exists($name, $exists_fields))
                            {
                                $identical = true;
                                $exf = $exists_fields[$name];
                                $ex_type = $exf['Type'];
                                $ex_db_basetype = getbasetype($ex_type); // тип без указания длины
                                $ex_db_length = getbaseLength($ex_type); // длина
                                $ex_null = $exf['Null'];
                                $ex_default = $exf['Default'];
                                $ex_extra = $exf['Extra'];
                                $identical_error = null;
                                if($ex_null != $null)
                                {
                                    $identical = false;
                                }
                                else
                                {
                                    if($ex_default != $default)
                                    {
                                        $identical = false;
                                    }
                                    else
                                    {
                                        $db_basetype = getbasetype($dbtype); // тип без указания длины
                                        $db_length = getbaseLength($dbtype); // длина
                                        if($db_basetype != $ex_db_basetype)
                                        {
                                             $identical = false;
                                        }
                                        else
                                        {
                                            if((!is_null($db_length) && ($db_length>0)) && ($db_length != $ex_db_length))
                                            {
                                                $identical = false;
                                            }
                                        }
                                    }
                                }
                                if($identical)
                                {
                                    // поле не нуждается в модификации
                                    $cancel = true;
                                }
                                else
                                {
                                    mysql_query($sql) or Exception2("Ошибка при модификации поля для таблицы $entityname.<br />".mysql_error());
                                    // echo "<li> MODIFY COLUMN $entityname $name [$identical_error]";
                                }
                            }
                            else
                            {
                                $sql = "ALTER TABLE `$entityname` ADD COLUMN $column";
                                mysql_query($sql) or Exception2("Ошибка при добавлении поля для таблицы $entityname.<br />".mysql_error());
                            }
                            /*if($unique)
                            {
                                @mysql_query("CREATE UNIQUE INDEX uidx_$name USING BTREE ON $entityname ($name);"); // or die("Ошибка создания индекса для поля `$name` сущности `$entityname` ".mysql_error());
                            }*/
                        }
                    }
                    // РАБОТА С ИНДЕКСАМИ
                    // Считываем имеющиеся индексы таблицы
                    $currentindexes = Array();
                    $r_indexes = mysql_query("SHOW INDEX FROM $entityname");
                    while($row = mysql_fetch_object($r_indexes))
                    {
                        switch($row->Key_name)
                        {
                            case "PRIMARY":
                                break;
                            default:
                                $currentindexes[$row->Key_name][] = $row->Column_name;
                                break;
                        }
                    }
                    // Здесь необходимо сравнить имеющиеся индексы и прочитанные из схемы,
                    // удалить удаленные, добавить новые и изменить существующие
                    // текущие индексы нах-ся в массиве $currentindexes, новые в массиве $indexes.
                    // Ищем удаленные индексы и удаляем их
                    foreach($currentindexes as $key => $indexfields)
                    {
                        if(array_key_exists($key, $indexes))
                        {
                            // Проверяем изменен ли индекс
                            $old_idx_fields = implode(",", $indexfields);
                            $new_idx_fields = implode(",", $indexes[$key]);
                            if($old_idx_fields == $new_idx_fields)
                            {
                                // индекс существует и он такой-же
                                unset($indexes[$key]);
                            }
                            else
                            {
                            // Удаляем индекс, т.к. он изменен в новой схеме
                            @mysql_query("ALTER TABLE $entityname DROP INDEX $key");
                            }
                        }
                        else
                        {
                            // Удаляем индекс, т.к. его нет в новой схеме
                            @mysql_query("ALTER TABLE $entityname DROP INDEX $key");
                        }
                    }
                    foreach($indexes as $indexname => $index)
                    {
                        $t_indexes = Array();
                        foreach($index as $fieldname)
                        {
                            $t_indexes[] = "`$fieldname`";
                        }
                        $indexedfields = implode(",", $t_indexes);
                        mysql_query("CREATE UNIQUE INDEX $indexname USING BTREE ON $entityname ($indexedfields);") or CriticalError("Ошибка в файле ".__FILE__." в строке ".__LINE__.", ".mysql_error(), null, true);
                    }
                // Запуск процедуры создания класса для сущности
                $this->CreateEntityClass($entityname, $entitydescription, $fields_ex);
            }
        }

        // Создание файла класса для определенной сущности
        public function CreateEntityClass($entityname, $entitydescription, $fields_ex)
        {
            $file = $this->entityfile_update;
            $fh = fopen($file, 'a+') or die("Can't open file");
            if($fh != 0)
            {
                // Список полей
                reset($fields_ex);
                $buf = Array();
                $fieldnames = Array();
                foreach($fields_ex as $field)
                {
                    $name = $field['name'];
                    // автоматическая сериализация массива свойств полей в строку
                    $field_as_array = var_export($field, true);
                    // убираем переносы строк
                    $field_as_array = str_replace("\r", '', $field_as_array);
                    $field_as_array = str_replace("\n", '', $field_as_array);
                    // убираем двойные пробелы
                    $field_as_array = str_replace('  ', ' ', $field_as_array);
                    $buf[] = "'$name'=>$field_as_array";
                    $fieldnames[] = '$'.$name;
                }
                $fieldnames2 = implode(',', $fieldnames);
                $fieldsbuf = implode(",\r\n      ", $buf);
                fwrite($fh,

                "<?php
                    class $entityname extends MikronBaseObject
                    {
                        private static \$description = '$entitydescription';
                        private static \$fields = Array($fieldsbuf);
                        public function fields()
                        {
                            return self::\$fields;
                        }
                        public function description()
                        {
                            return self::\$description;
                        }
                        public function __construct(\$mysql_row = null)
                        {
                            parent::__construct(\$mysql_row, __CLASS__, self::\$fields);
                        }
                        public function __get(\$var)
                        {
                            if(\$var=='fields'){return self::\$fields;}else{return parent::__get(\$var);}
                        }
                    }
                ?>"

                /*
                "<?php class $entityname extends T_BASE
                {
                    public $fieldnames2;
                    public static \$fields = Array($fieldsbuf);
                    public function __construct(\$id=null,\$readfields=null,\$parent=null,\$sqlrow=null)
                    {
                        self::\$mikron_class_description = \"$entitydescription\";
                        \$this->UpdateFields(\$id,\$readfields,\$parent,\$sqlrow);
                    }
                    public function fields()
                    {
                        return self::\$fields;
                    }
                    public function __get(\$var)
                    {
                        if(\$var==\"fields\"){return self::\$fields;}else{return parent::__get(\$var);}
                    }
                }
                ?>"*/
                );

                fclose($fh);
            }
        }
        
        function strtoupperx($txt)
        {  
            $txt = strtr($txt, "абвгдеёжзийклмнопрстуфхцчшщъыьэюя", "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"); 
            return($txt); 
        } 

        // система поиска
        function search($searchtext)
        {
            global $mikron;
            
            $file = constant('CMS_DIRECTORY').'/../modules/morphy/module.php';
            if(!file_exists($file))
            {
                throw new Exception('Файл модуля «morphy» не найден.');
            }
            require_once($file);

            $result = Array();
            $bulk_words = Array();
            $searchtext = $this->strtoupperx($searchtext);
            // Вытаскивание слов из текста
            // Определение: слова - непрерывная последовательность
            // букв анг. или русского алфавитов.
            preg_match_all('/([a-zA-Zа-яА-Я]+)/', $searchtext, $ok);
            // морфология
            foreach($ok[1] as $word)
            {
                // отсеиваем короткие слова, для более корректного поиска
                if(strlen($word) > 3)
                {
                    $bulk_words[] = $word;
                    // получить все словоформы:
                    // $all_forms = $morphy->getAllForms($bulk_words);
                    // получить псевдо-корень слова:
                    $pseudo_root = $morphy->getPseudoRoot($word);
                    foreach($pseudo_root as $pseudo_root_word)
                    {
                        // ищем псевдокорень слова подлиннее
                        if(strlen($pseudo_root_word) > 2)
                        {
                            $bulk_words[] = $pseudo_root_word;
                            break;
                        }
                    }
                }
            }
            // если нечего искать
            if(count($bulk_words) < 1)
            {
                return $result;
            }
            // откоректированная строка поиска
            $searchtext = '%'.implode('%', $bulk_words).'%';
            $searchtext_quoted = quote_smart($searchtext);
            // список сущностей
            $entities = $mikron->EntityManager->Items();
            foreach($entities as $entityname)
            {
                if(class_exists($entityname))
                {
                    $entity = new $entityname();
                    $entity_description = $entity->description();
                    $fields_for_search = Array();
                    foreach($entity->fields as $field)
                    {
                        if($field['forsearch'])
                        {
                            $fieldname = $field['name'];
                            $fields_for_search[] = "(UPPER(`$fieldname`) LIKE $searchtext_quoted)";
                        }
                    }
                    if(count($fields_for_search) > 0)
                    {
                        $conditions = implode(' OR ', $fields_for_search);
                        $queryresult = $mikron->Queries->Query($entityname, null, $conditions);
                        if($queryresult->count() > 0)
                        {
                            while($q = $queryresult->fetch())
                            {
                                $result[] = $q;
                            }
                        }
                    }
                }
            }
            return $result;
        }
    }

?>